protobuf: consecutive serialize and deserialize to

2019-07-15 13:07发布

问题:

My simple communication between C++ client and C# server got stuck after a message was serialized to socket (SerializeToFileDescritor).

C++ client:

  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# server

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

It seems to me ridiculously stupid it doesn't work.

If I remove any one of these: person.SerializeToFileDescriptor(s) or person2.ParseFromFileDescriptor(s), the other will work.

What should I do to make them work both?

回答1:

The default behaviour for a root object is to consume all data to the end of the stream. And since you don't close the stream, that end never comes.

If your intention is to send multiple objects down the same socket (which is pretty normal) then you need to give it a clue. The most common approach would be to prefix each message with the length of the data about to be sent. This could be in a fixed-32 int, or in a varint if convenient. You can then read that at the consumer.

How you then handle that depends on the caller; protobuf-net has a DeserializeWithLengthPrefix that will handle various forms of encoding, with-or-without an additional field marker (in the case of varint, to make it a valid protobuf stream). For example:

Person person = Serializer.DeserializeWithLengthPrefix<Person>(str,
        PrefixStyle.Fixed32, 0);


回答2:

If you intend to send multiple messages, this might be useful.