在 C# 中解析原始协议缓冲区字节流

Posted

技术标签:

【中文标题】在 C# 中解析原始协议缓冲区字节流【英文标题】:Parsing a raw Protocol Buffer byte stream in C# 【发布时间】:2013-10-29 00:08:56 【问题描述】:

给定一个编码为Streambyte[] 的协议缓冲区,但不知道对象类型本身,我们如何打印消息的骨架?该用例用于调试基于 protobuf 的 IO,用于根本原因分析。

如果有现有的工具可以从二进制文件中解析原始协议缓冲区字节流,那就太好了!另一种方法可能是使用 ProtoBuf.NET 类 ProtoReader() 继续运行,直到我们遇到错误,但 ProtoReader() 的用法尚不清楚。我从下面开始,但找不到关于如何使用 ProtoReader 类来实际执行它的好的文档。该项目的源代码也不是很容易理解......所以希望得到一些提示/帮助

using (var fs = File.OpenRead(filePath))

    using (var pr = new ProtoReader(fs, TypeModel.Create(), null))
    
        // Use ProtoReader to march through the bytes
        // Printing field number, type, size and payload values/bytes
    

【问题讨论】:

【参考方案1】:

首先,请注意,谷歌“protoc”命令行工具具有尝试反汇编没有架构信息的原始消息的选项。使用 protobuf-net,您可以执行以下操作 - 但我需要强调 没有架构,格式是不明确的:数据类型/格式比“有线类型”(实际编码格式)。这里我只是展示可能的解释,但还有其他方法可以解析相同的数据。

static void WriteTree(ProtoReader reader)

    while (reader.ReadFieldHeader() > 0)
    
        Console.WriteLine(reader.FieldNumber);
        Console.WriteLine(reader.WireType);
        switch (reader.WireType)
        
            case WireType.Variant:
                // warning: this appear to be wrong if the 
                // value was written signed ("zigzag") - to
                // read zigzag, add: pr.Hint(WireType.SignedVariant);
                Console.WriteLine(reader.ReadInt64());
                break;
            case WireType.String:
                // note: "string" here just means "some bytes"; could
                // be UTF-8, could be a BLOB, could be a "packed array",
                // or could be sub-object(s); showing UTF-8 for simplicity
                Console.WriteLine(reader.ReadString());
                break;
            case WireType.Fixed32:
                // could be an integer, but probably floating point
                Console.WriteLine(reader.ReadSingle());
                break;
            case WireType.Fixed64:
                // could be an integer, but probably floating point
                Console.WriteLine(reader.ReadDouble());
                break;
            case WireType.StartGroup:
                // one of 2 sub-object formats
                var tok = ProtoReader.StartSubItem(reader);
                WriteTree(reader);
                ProtoReader.EndSubItem(tok, reader);
                break;
            default:
                reader.SkipField();
                break;
        
    

或在 v3 中:https://***.com/a/64621670/23354

【讨论】:

马克,太好了。鉴于您的经验,您能否建议使用 Google 的protoc?我只是找不到有关它的文档。我确实使用了--decode_raw,但不清楚如何将二进制数组传递给它(例如:.bin 文件)... Failed to parse input.protoc 和以前一样,即使 ProtoBuf 能够很好地读取二进制文件/流。以为我可能缺少某些格式或参数,但现在猜。没关系,我将只使用 ProtoBuf.NET 代码。谢谢! @user100003 如果您认为数据是子对象,那么您基本上完全使用StartGroup 中显示的相同代码 - 即StartSubItem, @ 987654329@,EndSubItem。更正确的方法是将数据获取为byte[] (ProtoReader.AppendBytes),然后测试它是否正确解析为(单独)UTF-8 字符串、子留言等

以上是关于在 C# 中解析原始协议缓冲区字节流的主要内容,如果未能解决你的问题,请参考以下文章

TCP协议面试热点(面向字节流,粘包问题,TCP异常情况)

确定协议缓冲区消息类型的最佳实践

Java学习笔记6.1.3 字节流 - 字节流缓冲区与缓冲字节流

Java学习笔记6.1.3 字节流 - 字节流缓冲区与缓冲字节流

传输层的安全

Java IO流 - 缓冲流的详细使用介绍