Frage

Ich habe 2 vernetzte apps, sollte senden serialisiert protobuf-net Nachrichten.Ich kann die Serialisierung der Objekte und senden Sie Sie jedoch Ich kann nicht herausfinden, wie zu Deserialisieren der empfangenen bytes.

Ich versuchte, Sie zu Deserialisieren mit diesem, und es konnte nicht mit einer NullReferenceException.

// Where "ms" is a memorystream containing the serialized
// byte array from the network.
Messages.BaseMessage message =
  ProtoBuf.Serializer.Deserialize<Messages.BaseMessage>(ms);

Ich leite einen header vor dem serialisierten Byte, enthält message-ID-Typ, die ich verwenden kann, die in eine riesige switch-Anweisung zur Rückgabe des zu erwartenden Unterklasse-Typ.Mit dem block unten, erhalte ich den Fehler:System.Reflexion.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;

Hier ist die Funktion, die ich verwenden, um eine Nachricht zu senden, die über das Netzwerk:

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());
  }
}

Dies die Klasse, mit der Basis Klasse, ich bin zu serialisieren:

[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; }
    }
}


Feste mit Marc Gravell s Vorschlag.Ich entfernte die ProtoMember-Attribut aus dem readonly-Eigenschaften.Auch wechselte mit SerializeWithLengthPrefix.Hier ist, was ich habe jetzt:

[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; }
    }
}

So empfangen Sie ein Objekt:

//where "this.Ssl" is an SslStream.
BaseMessage message =
  ProtoBuf.Serializer.DeserializeWithLengthPrefix<BaseMessage>(
    this.Ssl, ProtoBuf.PrefixStyle.Base128);

Um ein Objekt zu senden:

//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);
War es hilfreich?

Lösung

Erste;für die Netznutzung, es ist SerializeWithLengthPrefix und DeserializeWithLengthPrefix die Länge Griff für Sie (wahlweise mit tag).Die MakeGenericMethod sieht OK aus auf den ersten Blick;und dies tatsächlich knüpft sehr eng an die ausstehenden übergabe, die Arbeit, die ich getan habe, um zu implementieren eine RPC-stack:die ausstehende Codes has an override of DeserializeWithLengthPrefix das dauert (im wesentlichen) einen Func<int,Type>, zu beheben , einen tag, um ein Typ zu, machen es einfacher zu Deserialisieren unerwartete Daten on the fly.

Wenn der message-Typ tatsächlich bezieht sich auf die Vererbung zwischen BaseMessage und BeginRequest,, dann brauchen Sie nicht diese;es geht immer an der Spitze-die meisten Vertrages Typ in der Hierarchie und arbeitet seinen Weg nach unten (wegen etwas Draht-details).

Auch - ich hatte noch keine Gelegenheit, es zu testen, aber die folgenden könnten verstörend sein, es:

[ProtoMember(1)]
public override UInt16 messageType
{
    get { return 1; }
}

Es ist gekennzeichnet für die Serialisierung, hat aber keinen Mechanismus für die Einstellung der Wert.Vielleicht ist das das Problem?Versuchen Sie, die [ProtoMember] hier, da ich diese nicht hilfreich ist - es ist (so weit wie Serialisierung betrifft), weitgehend eine Kopie des [ProtoInclude(...)] marker.

Andere Tipps

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

oder

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

Ein anderer Weg, um dies zu umgehen ist die Verwendung von protobuf-net für das "heavy lifting", sondern auch, um Ihre eigene Nachricht-header.Das problem mit der Verarbeitung von Netzwerk-Nachrichten ist, dass Sie gebrochen werden können, über die Grenzen hinweg.Dies erfordert in der Regel mit einem Puffer zu sammeln liest.Wenn Sie mit Ihrem eigenen Kopf, Sie können sicher sein, dass die Nachricht dort in Ihrer Gesamtheit vor der übergabe es zu protobuf-net.

Als Beispiel:

Senden

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);
}

Zu erhalten

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);
        }
    }
}

Die letztere Methode wäre "versucht," Sie auf ein Akku-Puffer, bis es genug Daten.Sobald die Größe Anforderung erfüllt ist, kann die Nachricht deserialisiert werden.

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top