Reputation: 474
I am trying to send mixed message types down a single channel with protobuf-net. I put together the following example from various sources, and it throws a StackOverflow exception on the first deserialization.
Am I going about this the right way?
FWIW the hex content of the file it creates is "A2 06 02 08 02 08 01 AA 06 02 08 04 08 03"
Thanks, Greg
[ProtoContract(ImplicitFields = ImplicitFields.AllFields)]
[ProtoInclude(100, typeof(Derived1))]
[ProtoInclude(101, typeof(Derived2))]
public class Base { public int Old; }
[ProtoContract(ImplicitFields = ImplicitFields.AllFields)]
public class Derived1 : Base { public int New1; }
[ProtoContract(ImplicitFields = ImplicitFields.AllFields)]
public class Derived2 : Base { public int New2; }
class Program
{
static void Main(string[] args)
{
Base b1 = new Derived1() { Old = 1, New1 = 2 };
Base b2 = new Derived2() { Old = 3, New2 = 4 };
using (var fs = File.Create(@"C:\test.bin"))
{
Serializer.Serialize(fs, b1);
Serializer.Serialize(fs, b2);
}
Base msg3, msg4;
using (var fs = File.OpenRead(@"C:\test.bin"))
{
msg3 = Serializer.Deserialize<Base>(fs);
msg4 = Serializer.Deserialize<Base>(fs);
}
Console.WriteLine(((Derived1)msg3).New1);
Console.WriteLine(((Derived2)msg4).New2);
Console.ReadLine();
}
}
Upvotes: 2
Views: 566
Reputation: 160
I had the same issue while deserializing, the release v2.1.0-alpha-1 fixes this.
https://github.com/mgravell/protobuf-net/releases
Regards,
Pluc
Upvotes: 0
Reputation: 1064234
I expect the problem here is framing; protobuf (the format, not the library) was designed by google to be appendable, where append===merge. If you serialize message A, then immediately serialize message B, then deserialize the whole lot: you get one message, not two. This obviously has lots of potential to cause unexpected results, incorrectly interpreted data, and errors.
The trick, then, is to frame the messages so that you can process them separately. Fortunately, protobuf-net provides the SerializeWithLengthPrefix
and DeserializeWithLengthPrefix
methods for exactly this purpose. During serialization, this prepends each message with a marker to indicate the length of the data that follows. During deserialization, the length-prefix is read first, allowing the correct amount of data to be consumed for each message.
Multiple layouts and styles of length-prefix are supported; the most important thing is that they match between the serialize and deserialize step. If you want the data to be "pure protobuf" (i.e. parseable as a repeated
field on an outer message on another platform), then "base-128, field 1" is a good choice.
This works fine:
using (var fs = File.Create(@"test.bin"))
{
Serializer.SerializeWithLengthPrefix(fs, b1, PrefixStyle.Base128, 1);
Serializer.SerializeWithLengthPrefix(fs, b2, PrefixStyle.Base128, 1);
}
Base msg3, msg4;
using (var fs = File.OpenRead(@"test.bin"))
{
msg3 = Serializer.DeserializeWithLengthPrefix<Base>(fs, PrefixStyle.Base128, 1);
msg4 = Serializer.DeserializeWithLengthPrefix<Base>(fs, PrefixStyle.Base128, 1);
}
Upvotes: 2