문제

직렬화된 protobuf-net 메시지를 서로 보내야 하는 2개의 네트워크 앱이 있습니다.객체를 직렬화하여 보낼 수는 있지만, 수신된 바이트를 역직렬화하는 방법을 알 수 없습니다..

이것을 역직렬화하려고 했으나 NullReferenceException으로 인해 실패했습니다.

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

예상되는 하위 유형을 반환하기 위해 거대한 스위치 문에서 사용할 수 있는 메시지 유형 ID가 포함된 직렬화된 바이트 앞에 헤더를 전달합니다.아래 블록을 사용하면 오류가 발생합니다.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;

다음은 네트워크를 통해 메시지를 보내는 데 사용하는 기능입니다.

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의 제안을 사용합니다.읽기 전용 속성에서 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);
도움이 되었습니까?

해결책

첫 번째;네트워크 사용을 위해 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(...)] 채점자.

다른 팁

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

또는

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

이를 처리하는 또 다른 방법은 "무거운 리프팅"에 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);
        }
    }
}

후자의 방법은 충분한 데이터가있을 때까지 축합기 버퍼에서 "시도"됩니다. 크기 요구 사항이 충족되면 메시지를 사로화 할 수 있습니다.

라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top