协议缓冲区从原始消息中检测类型

本文关键字:检测 类型 消息 原始 缓冲区 协议 | 更新日期: 2023-09-27 18:22:13

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

我有一种情况,端点可以接收不同的消息,并且我需要能够检测类型,然后才能对其进行反序列化

我正在使用protobuf net

协议缓冲区从原始消息中检测类型

您不能孤立地检测类型,因为protobuf规范没有为此向流中添加任何数据;然而,根据上下文的不同,有很多方法可以使这变得容易:

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

最后一种方法在原始TCP流的情况下实际上是非常有价值的;这是导线上的与并集类型相同,但具有不同的实现;通过预先决定1=Foo,2=Bar等(与联合类型方法完全相同),可以使用SerializeWithLengthPrefix进行写入(将1/2/etc指定为字段号),并使用非通用TryDeserializeWithLengthPrefix进行读取(这在v1 API中的Serializer.NonGeneric下,或在v2 API中的TypeModel上),从而反序列化正确的类型。为了避免"为什么这对TCP流有用?"这个问题,因为:在正在进行的TCP流中,您需要无论如何使用WithLengthPrefix方法,以避免过度读取流;因此,您还可以免费获得类型标识符!

摘要:

  • 联合类型:易于实现;只有下一方必须检查哪些属性不是null
  • 继承:易于实现;可以使用多态性或鉴别器来处理"现在是什么?"
  • type prefix:实现起来有点麻烦,但允许更大的灵活性,并且在TCP流上没有开销

一个典型的选项是使用包装消息作为"选项类型"或区分的并集。您可以有一个枚举(每个消息类型一个)和一个包含消息类型的字段的消息,然后每个消息类型有一个可选字段。

Protobuf文档中将其描述为"联合类型"。

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

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

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

您可以使用一种名为"自描述消息"的技术。它可以用于生成一组.proto文件,描述包装中编码为"any"的每个消息类型。文档中的一个示例:

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中可用。