I have 2 networked apps that should send serialized protobuf-net messages to each other. I can serialize the objects and send them, however, I cannot figure out how to deserialize the received bytes.
I tried to deserialize with this and it failed with a NullReferenceException.
// Where 'ms' is a memorystream containing the serialized // byte array from the network. Messages.BaseMessage message = ProtoBuf.Serializer.Deserialize<Messages.BaseMessage>(ms);
I am passing a header before the serialized bytes that contains message type ID, which I can use in a giant switch statement to return the expected sublcass Type. With the block below, I receive the error: System.Reflection.TargetInvocationException —> System.NullReferenceException.
//Where 'ms' is a memorystream and 'messageType' is a //Uint16. Type t = Messages.Helper.GetMessageType(messageType); System.Reflection.MethodInfo method = typeof(ProtoBuf.Serializer).GetMethod('Deserialize').MakeGenericMethod(t); message = method.Invoke(null, new object[] { ms }) as Messages.BaseMessage;
Here’s the function I use to send a message over the network:
internal void Send(Messages.BaseMessage message){ using (System.IO.MemoryStream ms = new System.IO.MemoryStream()){ ProtoBuf.Serializer.Serialize(ms, message); byte[] messageTypeAndLength = new byte[4]; Buffer.BlockCopy(BitConverter.GetBytes(message.messageType), 0, messageTypeAndLength, 0, 2); Buffer.BlockCopy(BitConverter.GetBytes((UInt16)ms.Length), 0, messageTypeAndLength, 2, 2); this.networkStream.Write(messageTypeAndLength); this.networkStream.Write(ms.ToArray()); } }
This the class, with base class, I’m serializing:
[Serializable, ProtoContract, ProtoInclude(50, typeof(BeginRequest))] abstract internal class BaseMessage { [ProtoMember(1)] abstract public UInt16 messageType { get; } } [Serializable, ProtoContract] internal class BeginRequest : BaseMessage { [ProtoMember(1)] public override UInt16 messageType { get { return 1; } } }
Fixed using Marc Gravell’s suggestion. I removed the ProtoMember attribute from the readonly properties. Also switched to using SerializeWithLengthPrefix. Here’s what I have now:
[Serializable, ProtoContract, ProtoInclude(50, typeof(BeginRequest))] abstract internal class BaseMessage { abstract public UInt16 messageType { get; } } [Serializable, ProtoContract] internal class BeginRequest : BaseMessage { public override UInt16 messageType { get { return 1; } } }
To receive an object:
//where 'this.Ssl' is an SslStream. BaseMessage message = ProtoBuf.Serializer.DeserializeWithLengthPrefix<BaseMessage>( this.Ssl, ProtoBuf.PrefixStyle.Base128);
To send an object:
//where 'this.Ssl' is an SslStream and 'message' can be anything that // inherits from BaseMessage. ProtoBuf.Serializer.SerializeWithLengthPrefix<BaseMessage>( this.Ssl, message, ProtoBuf.PrefixStyle.Base128);
First; for network usage, there is
SerializeWithLengthPrefixandDeserializeWithLengthPrefixwhich handle length for you (optionally with a tag). TheMakeGenericMethodlooks OK at first glance; and this actually ties in very closely to the pending commit of the work I’ve been doing to implement an RPC stack: the pending codehas an override of DeserializeWithLengthPrefixthat takes (essentially) aFunc<int,Type>, to resolve a tag to a type to make it easier to deserialize unexpected data on the fly.If the message type actually relates to the inheritance between
BaseMessageandBeginRequest, then you don’t need this; it always goes to the top-most contract type in the hierarchy and works its way down (due to some wire details).Also – I haven’t had chance to test it, but the following might be upsetting it:
It is marked for serialization, but has no mechanism for setting the value. Maybe this is the issue? Try removing the
[ProtoMember]here, since I don’t this is useful – it is (as far as serialization is concerned), largely a duplicate of the[ProtoInclude(...)]marker.