【问题标题】:Protocol buffers detect type from raw message协议缓冲区从原始消息中检测类型
【发布时间】:2012-02-25 15:43:49
【问题描述】:

是否可以检测原始协议缓冲区消息的类型(以字节[]为单位)

我有一种情况,端点可以接收不同的消息,我需要能够在反序列化之前检测到类型。

我正在使用 protobuf-net

【问题讨论】:

  • 我只使用多个端点。函数重载在编程语言中已经够笨重了,更不用说在网络服务中了。
  • 谢谢,这是我的备用计划 :)

标签: c# .net protocol-buffers protobuf-net


【解决方案1】:

一个典型的选项是让包装消息充当“选项类型”或可区分的联合。你可以有一个枚举(每个消息类型一个)和一个包含消息类型的字段的消息,然后每个消息类型有一个可选字段。

这在 Protobuf 文档中被描述为 "union type"

【讨论】:

    【解决方案2】:

    您无法单独检测类型,因为 protobuf 规范没有为此向流中添加任何数据;但是,根据具体情况,有多种方法可以简化此操作:

    • 联合类型(如 Jon 所述)涵盖了一系列场景
    • 继承(特定于 protobuf-net)可以是通用的 - 您可以拥有基本消息类型和任意数量的具体消息类型
    • 可以使用前缀来表示传入类型

    最后一种方法在原始 TCP 流的情况下实际上非常有价值;这在线与联合类型相同,但实现不同;通过预先决定 1=Foo, 2=Bar 等(就像您对联合类型方法所做的那样),您可以使用SerializeWithLengthPrefix 来编写(指定 1/2/etc 作为字段编号),并且非-generic TryDeserializeWithLengthPrefix 读取(这是在 v1 API 中的 Serializer.NonGeneric 下,或在 v2 API 中的 TypeModel 下),您可以提供将数字解析回类型的类型映射,从而反序列化正确的类型.并预先提出“为什么这对 TCP 流有用?”这个问题。 - 因为:在持续的 TCP 流中,您需要使用WithLengthPrefix 方法无论如何,以避免过度读取流;所以你不妨免费获得类型标识符!

    总结:

    • 联合类型:易于实现;唯一的缺点是必须检查哪些属性是非空的
    • 继承:易于实现;可以使用多态性或判别器来处理“现在是什么?”
    • 类型前缀:实现起来有点繁琐,但提供了更大的灵活性,并且 TCP 流的开销为零

    【讨论】:

    • 您好,感谢您的详细回复。我已经设法使用 Serializer.SerializeWithLengthPrefix 和 Serializer.NonGeneric.TryDeserializeWithLengthPrefix 来实现所需的结果我想使用 RuntimeTypeModel 来避免在我的类上使用属性。 model.SerializeWithLengthPrefix 似乎按预期工作,但我在 RuntimeTypeModel 上找不到 TryDeserializeWithLengthPrefix 的等效项。 model.DeserializeWithLengthPrefix 似乎需要一个类型。我应该如何使用模型完成等效的 TryDeserializeWithLengthPrefix 。使用版本 2.0.0.480 nuget
    • 我已更改我的代码以添加以将模型定义添加到 RuntimeTypeModel.Default 并且我可以使用不带属性的 TryDeserializeWithLengthPrefix。在我使用 TypeModel.Create 创建新模型之前,问题解决了,谢谢
    • @Yavor 在 TypeModel 上,它只是 DeserializeWithLengthPrefix,以及一个 TypeResolver。不知道 Try 去了哪里!我会在沙发上的靠垫后面找它——大多数事情都在那里。
    【解决方案3】:

    你可以这样包装。数据将保存实际消息的位置。

    message MyCustomProtocol {
      required int32 protocolVersion = 1;
      required int32 messageType = 2;
      bytes data = 3;
    }
    

    协议的一般规则是包含协议版本。一旦您有新老客户,您会很高兴拥有它。

    【讨论】:

      【解决方案4】:

      您可以使用一种称为Self Describing Messages 的技术。它可用于生成一组 .proto 文件,描述包装器中编码为“任何”的每种消息类型。文档中的一个示例:

      syntax = "proto3";
      
      import "google/protobuf/any.proto";
      import "google/protobuf/descriptor.proto";
      
      message SelfDescribingMessage {
        // Set of FileDescriptorProtos which describe the type and its dependencies.
        google.protobuf.FileDescriptorSet descriptor_set = 1;
      
        // The message and its type, encoded as an Any message.
        google.protobuf.Any message = 2;
      }
      

      应该注意的是,在撰写此响应时,对这些消息的本机支持仅在 C++ 和 Java 中可用。

      【讨论】:

        猜你喜欢
        • 2013-02-21
        • 2022-07-27
        • 1970-01-01
        • 2013-09-01
        • 1970-01-01
        • 1970-01-01
        • 2022-01-20
        • 1970-01-01
        • 2019-01-04
        相关资源
        最近更新 更多