Domanda

Ho 2 applicazioni di rete che dovrebbe inviare serializzato protobuf-net di messaggi.Posso serializzare gli oggetti e li invia, tuttavia, Non riesco a capire come deserializzare i byte ricevuti.

Ho provato a deserializzare con questo e non è riuscito con una NullReferenceException.

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

Sto passando un header prima serializzato byte che contiene l'ID del tipo di messaggio, che posso utilizzare in un gigante istruzione switch per restituire i sublcass Tipo.Con il blocco di seguito, ricevo l'errore:Sistema.La riflessione.TargetInvocationException ---> Sistema.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;

Ecco la funzione che ho utilizzato per inviare un messaggio attraverso la rete:

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

Questa classe, con classe di base, sono serializzazione:

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


Fisso con Marc Gravell suggerimento.Ho rimosso il ProtoMember attributo readonly proprietà.Anche passati all'utilizzo di SerializeWithLengthPrefix.Ecco quello che ho ora:

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

Per ricevere un oggetto:

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

Per inviare un oggetto:

//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);
È stato utile?

Soluzione

Primo;per l'utilizzo della rete, c'è SerializeWithLengthPrefix e DeserializeWithLengthPrefix cui maniglia lunghezza (opzionalmente con un tag).Il MakeGenericMethod sembra OK, a prima vista;e questo, in effetti, lega molto strettamente in attesa di commettere il lavoro che ho fatto per implementare uno stack RPC:in attesa di codice has an override of DeserializeWithLengthPrefix che prende (essenzialmente) un Func<int,Type>, per risolvere un tag a un tipo per rendere più facile per deserializzare i dati inaspettati sul fly.

Se il tipo di messaggio, in realtà si riferisce all'eredità tra BaseMessage e BeginRequest, quindi non avete bisogno di questo;si va sempre più in alto, tipo di contratto, nella gerarchia, e lavora la sua strada verso il basso (a causa di alcuni dettagli del bonifico).

Anche io non ho avuto la possibilità di provarlo, ma il seguito potrebbe essere sconvolgente è:

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

Esso è contrassegnato per la serializzazione, ma non ha il meccanismo per la regolazione del valore.Forse è questo il problema?Provare a rimuovere il [ProtoMember] qui, dal momento che non è utile, è (per quanto riguarda la serializzazione è interessato), in gran parte un duplicato del [ProtoInclude(...)] marcatore.

Altri suggerimenti

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

o

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

Un altro modo per gestire questa situazione è quello di utilizzare protobuf-net per il "pesante", ma di utilizzare la propria intestazione del messaggio.Il problema con l'elaborazione di messaggi di rete è che si può essere rotto al di là dei confini.Questo di solito richiede l'uso di un buffer di accumulare legge.Se si utilizza il tuo header, si può essere sicuri che il messaggio è lì, nella sua interezza, prima di dare il via ad protobuf-net.

Come esempio:

Per inviare

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

Per ricevere

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

Il secondo metodo potrebbe essere "provato" a un accumulatore tampone fino a quando c'era una quantità sufficiente di dati.Una volta che la dimensione del requisito, il messaggio può essere la deserializzazione.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top