protobuf: последовательная сериализация и десериализация в / из сокета
Мое простое взаимодействие между клиентом C++ и сервером C# застряло после сериализации сообщения в сокет (SerializeToFileDescritor).
Клиент C++:
Person person;
person.set_id(54321);
person.set_name("bla");
person.mutable_address()->set_line1("sdfa");
person.mutable_address()->set_line2("asdfsdfa");
cout << person.id() << endl << person.name() << endl;
cout << person.address().line2() << endl;
person.SerializeToFileDescriptor(s);
ZeroCopyInputStream* raw_input = new FileInputStream(s);
CodedInputStream* coded_input = new CodedInputStream(raw_input);
Person person2;
person2.ParseFromFileDescriptor(s);
cout << person2.id() << endl << person2.name() << endl;
cout << person2.address().line2() << endl;
C# сервер
var sockServer = new TcpListener(2048);
sockServer.Start();
var person = new Person { Id = 123456, Name = "Fred", Address = new Address { Line1 = "Flat 1", Line2 = "The Meadows ar garą " } };
var socket = sockServer.AcceptSocket();
Stream str = new NetworkStream(socket);
var response = Serializer.Deserialize<Person>(str);
Console.WriteLine(response.Id);
Serializer.Serialize(str, person);
Мне кажется смешно глупо, что это не работает.
Если я удалю один из них: person.SerializeToFileDescriptor(s) или person2.ParseFromFileDescriptor(s), другой будет работать.
Что я должен сделать, чтобы заставить их работать оба?
2 ответа
Поведение по умолчанию для корневого объекта заключается в использовании всех данных до конца потока. И поскольку вы не закрываете поток, этот конец никогда не наступит.
Если вы намереваетесь отправить несколько объектов по одному сокету (что вполне нормально), вам нужно дать ему подсказку. Наиболее распространенный подход заключается в добавлении к каждому сообщению длины данных, которые должны быть отправлены. Это может быть в фиксированном int 32 или в varint, если это удобно. Вы можете прочитать это у потребителя.
Как вы справляетесь с этим, зависит от абонента; Protobuf-сеть имеет DeserializeWithLengthPrefix
это будет обрабатывать различные формы кодирования, с дополнительным маркером поля или без него (в случае varint, чтобы сделать его допустимым потоком protobuf). Например:
Person person = Serializer.DeserializeWithLengthPrefix<Person>(str,
PrefixStyle.Fixed32, 0);
Если вы собираетесь отправлять несколько сообщений, это может быть полезно.