我有2个网络应用程序,应该相互发送序列化的protobuf-net消息。我可以序列化对象并发送它们,但是,我无法弄清楚如何反序列化接收的字节。
我尝试使用此反序列化,但它因NullReferenceException而失败。
// Where "ms" is a memorystream containing the serialized
// byte array from the network.
Messages.BaseMessage message =
ProtoBuf.Serializer.Deserialize<Messages.BaseMessage>(ms);
我在包含消息类型ID的序列化字节之前传递一个标头,我可以在一个巨大的switch语句中使用它来返回预期的sublcass Type。使用下面的块,我收到错误:System.Reflection.TargetInvocationException ---&gt; 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;
这是我用来通过网络发送消息的功能:
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());
}
}
这个类,带有基类,我正在序列化:
[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; }
}
}
<小时/> 修正了使用Marc Gravell的建议。我从readonly属性中删除了ProtoMember属性。也切换到使用SerializeWithLengthPrefix。这就是我现在所拥有的:
[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; }
}
}
收到一个对象:
//where "this.Ssl" is an SslStream.
BaseMessage message =
ProtoBuf.Serializer.DeserializeWithLengthPrefix<BaseMessage>(
this.Ssl, ProtoBuf.PrefixStyle.Base128);
发送对象:
//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);
答案 0 :(得分:9)
Serializer.NonGeneric.Deserialize(Type, Stream); //Thanks, Marc.
或
RuntimeTypeModel.Default.Deserialize(Stream, null, Type);
答案 1 :(得分:7)
首先;对于网络使用,有SerializeWithLengthPrefix
和DeserializeWithLengthPrefix
处理长度(可选择带标记)。 MakeGenericMethod
乍一看看起来不错;这实际上与我为实现RPC堆栈所做的工作的挂起提交密切相关:待处理代码has an override of DeserializeWithLengthPrefix
(基本上)Func<int,Type>
,将标记解析为一种类型,可以更轻松地动态反序列化意外数据。
如果消息类型实际上与BaseMessage
和BeginRequest
之间的继承有关,那么您不需要这个;它总是进入层次结构中最顶层的合同类型并且向下运行(由于一些电线细节)。
另外 - 我没有机会测试它,但以下可能会让它感到不安:
[ProtoMember(1)]
public override UInt16 messageType
{
get { return 1; }
}
标记为序列化,但没有设置值的机制。也许这就是问题?尝试删除此处的[ProtoMember]
,因为我认为这不是很有用 - 它(就序列化而言)很大程度上是[ProtoInclude(...)]
标记的重复。
答案 2 :(得分:3)
处理此问题的另一种方法是使用protobuf-net进行“繁重的工作”,但要使用自己的邮件标题。处理网络消息的问题在于它们可以跨越边界。这通常需要使用缓冲区来累积读数。如果您使用自己的标题,您可以确保在将其发送到protobuf-net之前完整地显示该消息。
举个例子:
发送
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);
}
接收
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);
}
}
}
后一种方法将在累加器缓冲区上“尝试”,直到有足够的数据。一旦满足大小要求,就可以反序列化消息。