Deserialize unknown type with protobuf-net

荒凉一梦 提交于 2019-11-27 12:07:40

First; for network usage, there is SerializeWithLengthPrefix and DeserializeWithLengthPrefix which handle length for you (optionally with a tag). The MakeGenericMethod looks 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 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.

If the message type actually relates to the inheritance between BaseMessage and BeginRequest, 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:

[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.

Serializer.NonGeneric.Deserialize(Type, Stream); //Thanks,  Marc.

or

RuntimeTypeModel.Default.Deserialize(Stream, null, Type); 

Another way to handle this is to use protobuf-net for the "heavy lifting", but to use your own message 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.

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.

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!