简体   繁体   English

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

[英]Protocol buffers detect type from raw message

Is it possible to detect the type of a raw protocol buffer message (in byte[]) 是否可以检测原始协议缓冲区消息的类型(在byte []中)

I have a situation where an endpoint can receive different messages and I need to be able to detect the type before I can deserialize it. 我有一种情况,端点可以接收不同的消息,我需要能够检测类型,然后才能反序列化它。

I am using protobuf-net 我正在使用protobuf-net

You can't detect the type in isolation, since the protobuf spec doesn't add any data to the stream for this; 您不能孤立地检测类型,因为protobuf规范不会为此向流添加任何数据; however, there are a number of ways of making this easy, depending on the context: 但是,根据具体情况,有很多方法可以简化:

  • a union type (as mentioned by Jon) covers a range of scenarios 联合类型(如Jon所述)涵盖了一系列场景
  • inheritance (protobuf-net specific) can be versatile - you can have a base-message type, and any number of concrete message types 继承(protobuf-net specific)可以是通用的 - 你可以有一个基本消息类型,以及任意数量的具体消息类型
  • you can use a prefix to indicate the incoming type 您可以使用前缀来指示传入类型

the last approach is actually very valuable in the case of raw TCP streams; 在原始TCP流的情况下,最后一种方法实际上非常有价值; this is on the wire identical to the union type, but with a different implementation; 与导线类型相同,但具有不同的实现; by deciding in advance that 1=Foo, 2=Bar etc (exactly as you do for the union type approach), you can use SerializeWithLengthPrefix to write (specifying the 1/2/etc as the field number), and the non-generic TryDeserializeWithLengthPrefix to read (this is under Serializer.NonGeneric in the v1 API, or on the TypeModel in the v2 API), you can provide a type-map that resolves the numbers back to types, and hence deserialize the correct type. 通过预先确定1 = Foo,2 = Bar等(与联合类型方法完全相同),您可以使用SerializeWithLengthPrefix进行写入(将1/2 / etc指定为字段编号),以及非泛型要读取TryDeserializeWithLengthPrefix (这是在v1 API中的Serializer.NonGeneric下,或者在v2 API中的TypeModel下),您可以提供一个类型映射,将数字解析回类型,从而反序列化正确的类型。 And to pre-empt the question "why is this useful with TCP streams?" 并预先解决“为什么这对TCP流有用?” - because: in an ongoing TCP stream you need to use the WithLengthPrefix methods anyway , to avoid over-reading the stream; - 因为:在正在进行的TCP流中, 无论如何需要使用WithLengthPrefix方法,以避免过度读取流; so you might as well get the type identifier for free! 所以你不妨免费获得类型标识符!

summary: 摘要:

  • union type: easy to implement; 联合类型:易于实施; only down side is having to then check which of the properties is non-null 只有下方必须检查哪些属性是非空的
  • inheritance: easy to implement; 继承:易于实现; can use polymorphism or discriminator to handle "what now?" 可以使用多态或鉴别器来处理“现在怎么办?”
  • type prefix: a bit more fiddly to implement, but allows more flexibility, and has zero overhead on TCP streams 类型前缀:实现起来有点繁琐,但允许更多的灵活性,并且在TCP流上没有任何开销

One typical option is to have a wrapper message to act as an "option type" or discriminated union. 一个典型的选择是使包装器消息充当“选项类型”或区别联合。 You could have an enum (one per message type) and a message containing a field with the message type in, and then one optional field per message type. 您可以拥有一个枚举(每个消息类型一个)和一个包含消息类型的字段的消息,然后每个消息类型一个可选字段。

This is described in the Protobuf documentation as a "union type" . 这在Protobuf文档中描述为“联合类型”

You could wrap it like this. 你可以像这样包装它。 Where data would hold the actual message. 数据将保存实际消息的位置。

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

A general rule for protocols is to include a protocol version. 协议的一般规则是包括协议版本。 You will be very happy to have it once you have old and new clients. 一旦有新老客户,您将非常乐意拥有它。

You could use a technique called Self Describing Messages . 您可以使用一种名为Self Describing Messages的技术。 It can be used to generate a set of .proto files describing each message type encoded as 'any' within a wrapper. 它可用于生成一组.proto文件,这些文件描述在包装器中编码为“任何”的每种消息类型。 An example from the docs: 来自文档的一个例子:

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;
}

It should be noted that native support for these messages at the time of writing this response is only available in C++ and Java. 应该注意的是,在编写此响应时对这些消息的本机支持仅在C ++和Java中可用。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM