[英]Deserialize unknown type with protobuf-net
I have 2 networked apps that should send serialized protobuf-net messages to each other.我有 2 个联网应用程序,它们应该相互发送序列化的 protobuf-net 消息。 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.我试图用这个反序列化,但它失败了一个 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.我在包含消息类型 ID 的序列化字节之前传递了一个 header,我可以在一个巨大的 switch 语句中使用它来返回预期的子类类型。 With the block below, I receive the error: System.Reflection.TargetInvocationException ---> System.NullReferenceException.
使用下面的块,我收到错误: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:这是我用来通过网络发送消息的 function:
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:这是 class,带有基础 class,我正在序列化:
[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; }
}
}
[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:要接收 object:
//where "this.Ssl" is an SslStream. BaseMessage message = ProtoBuf.Serializer.DeserializeWithLengthPrefix<BaseMessage>( this.Ssl, ProtoBuf.PrefixStyle.Base128);
To send an object:要发送 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);
Serializer.NonGeneric.Deserialize(Type, Stream); //Thanks, Marc.
or或者
RuntimeTypeModel.Default.Deserialize(Stream, null, Type);
First;第一的; for network usage, there is
SerializeWithLengthPrefix
and DeserializeWithLengthPrefix
which handle length for you (optionally with a tag).对于网络使用,有
SerializeWithLengthPrefix
和DeserializeWithLengthPrefix
可以为您处理长度(可选地带有标签)。 The MakeGenericMethod
looks OK at first glance; MakeGenericMethod
乍一看还不错; 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 code has an override of DeserializeWithLengthPrefix
that takes (essentially) a Func<int,Type>
, to resolve a tag to a type to make it easier to deserialize unexpected data on the fly.这实际上与我为实现 RPC 堆栈所做的工作的挂起提交密切相关:挂起的代码
has an override of DeserializeWithLengthPrefix
,它(本质上)采用Func<int,Type>
来解析标签到一种类型,以便更轻松地动态反序列化意外数据。
If the message type actually relates to the inheritance between BaseMessage
and BeginRequest
, then you don't need this;如果消息类型实际上与
BaseMessage
和BeginRequest
之间的 inheritance 相关,那么你不需要这个; 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:另外-我还没有机会对其进行测试,但是以下内容可能会令人不安:
[ProtoMember(1)]
public override UInt16 messageType
{
get { return 1; }
}
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.尝试在此处删除
[ProtoMember]
,因为我不认为这很有用 - 它(就序列化而言)很大程度上是[ProtoInclude(...)]
标记的副本。
Another way to handle this is to use protobuf-net for the "heavy lifting", but to use your own message header.处理此问题的另一种方法是使用 protobuf-net 进行“繁重的工作”,但使用您自己的消息 header。 The problem with processing network messages is that they can be broken across boundaries.
处理网络消息的问题是它们可以跨越边界被打破。 This typically requires using a buffer to accumulate reads.
这通常需要使用缓冲区来累积读取。 If you use your own header, you can be sure that the message is there in its entirety before handing it off to protobuf-net.
如果您使用自己的 header,则可以确保在将消息传递给 protobuf-net 之前完整地存在该消息。
As an example:举个例子:
To send发送
using (System.IO.MemoryStream ms = new System.IO.MemoryStream())
{
MyMessage message = new MyMessage();
ProtoBuf.Serializer.Serialize<BaseMessage>(ms, message);
byte[] buffer = ms.ToArray();
int messageType = (int)MessageType.MyMessage;
_socket.Send(BitConverter.GetBytes(messageType));
_socket.Send(BitConverter.GetBytes(buffer.Length));
_socket.Send(buffer);
}
To receive受到
protected bool EvaluateBuffer(byte[] buffer, int length)
{
if (length < 8)
{
return false;
}
MessageType messageType = (MessageType)BitConverter.ToInt32(buffer, 0);
int size = BitConverter.ToInt32(buffer, 4);
if (length < size + 8)
{
return false;
}
using (MemoryStream memoryStream = new MemoryStream(buffer))
{
memoryStream.Seek(8, SeekOrigin.Begin);
if (messageType == MessageType.MyMessage)
{
MyMessage message =
ProtoBuf.Serializer.Deserialize<MyMessage>(memoryStream);
}
}
}
The latter method would be "tried" on an accumulator buffer until there was enough data.后一种方法将在累加器缓冲区上“尝试”,直到有足够的数据。 Once the size requirement is met, the message can be deserialized.
一旦满足大小要求,就可以对消息进行反序列化。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.