Буферы протокола определяют тип из необработанного сообщения - PullRequest
14 голосов
/ 03 февраля 2012

Возможно ли определить тип необработанного сообщения буфера протокола (в байтах [])Я могу десериализовать его.

Я использую protobuf-net

Ответы [ 4 ]

15 голосов
/ 03 февраля 2012

Вы не можете обнаружить тип изолированно, так как спецификация protobuf не добавляет для этого никаких данных в поток; однако есть несколько способов сделать это легко, в зависимости от контекста:

  • тип объединения (как упоминал Джон) охватывает ряд сценариев
  • наследование (специфично для protobuf-net) может быть универсальным - вы можете иметь базовый тип сообщения и любое количество конкретных типов сообщений
  • вы можете использовать префикс для указания типа входящего

последний подход на самом деле очень полезен в случае необработанных потоков TCP; на проводе идентичен типу объединения, но с другой реализацией; заранее решив, что 1 = Foo, 2 = Bar и т. д. (точно так же, как вы делаете для подхода типа объединения), вы можете использовать SerializeWithLengthPrefix для записи (указав 1/2 / etc в качестве номера поля), а не -generic TryDeserializeWithLengthPrefix для чтения (это в Serializer.NonGeneric в API v1 или в TypeModel в API v2), вы можете предоставить карту типов, которая разрешает числа обратно в типы и, следовательно, десериализует правильный тип , И чтобы предупредить вопрос "почему это полезно с потоками TCP?" - потому что: в текущем потоке TCP вам необходимо , чтобы использовать WithLengthPrefix методы в любом случае , чтобы избежать перечитывания потока; так что вы можете получить идентификатор типа бесплатно!

Резюме:

  • тип соединения: легко реализовать; только нижняя сторона должна затем проверить, какое из свойств не является ненулевым
  • наследование: легко реализовать; может использовать полиморфизм или дискриминатор для обработки «что теперь?»
  • префикс типа: немного сложнее в реализации, но обеспечивает большую гибкость и не требует дополнительных затрат на потоки TCP
12 голосов
/ 03 февраля 2012

Одним из типичных вариантов является сообщение-обертка, действующее как «тип параметра» или дискриминируемый союз.Вы можете иметь enum (по одному на тип сообщения) и сообщение, содержащее поле с типом сообщения in, а затем одно необязательное поле для типа сообщения.

Это описано в документации Protobuf как "тип соединения" .

3 голосов
/ 13 ноября 2013

Вы можете обернуть это так. Где данные будут содержать фактическое сообщение.

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

Общее правило для протоколов состоит в том, чтобы включать версию протокола. Вы будете очень рады иметь ее, как только у вас будут старые и новые клиенты.

1 голос
/ 09 октября 2018

Вы можете использовать технику под названием Сообщения с самоописанием .Его можно использовать для генерации набора файлов .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.

...