3

クライアント間でデータを中継するためにパブ/サブサーバーとして使用されるトランスポートアプリケーションがあります。pub/sub サーバーは、各データについて少し知っているだけで済みます。たとえば、発行されたトピックを正しいサブスクライバーに中継できるようにするために、トピック名が必要です。

これを実現するために、ProtoContract として装飾されたクラスに、protobuf-net のシリアル化されたデータを含む byte[] を含めるスキームを考えました。この方法では、サーバーはリレーするデータのごく一部を逆シリアル化するだけで済みます (型を知る必要はありません)。私のタイプは次のようになります。

[ProtoContract]
public class DataFrame
{
    /// <summary>
    /// Time the data was issued or sampled. In UTC time.
    /// </summary>
    [ProtoMember(1)]
    public DateTime TimeStamp = DateTime.UtcNow;

    /// <summary>
    /// The topic associated with the data
    /// </summary>
    [ProtoMember(2)]
    public string TopicName = string.Empty;

    /// <summary>
    /// Command, can be either Discover, Subscribe, Unsubscribe or Publish
    /// </summary>
    [ProtoMember(3)]
    public string Command = string.Empty;

    /// <summary>
    /// The fully qualified type name of the content
    /// </summary>
    [ProtoMember(4)]
    private string _typeName = string.Empty;

    /// <summary>
    /// Serialized content data (if any)
    /// </summary>
    [ProtoMember(5)]
    private byte[] _content;

    /// <summary>
    /// The fully qualified type name of the content
    /// </summary>
    public string TypeName
    {
        get
        {
            return _typeName;
        }
    }

    /// <summary>
    /// Get the content of this DataFrame
    /// </summary>
    /// <typeparam name="T">Type of the content</typeparam>
    /// <returns>The content</returns>
    public T GetContent<T>()
    {
        MemoryStream ms = new MemoryStream(_content);
        return Serializer.DeserializeWithLengthPrefix<T>(ms, PrefixStyle.Base128);
    }

    /// <summary>
    /// Set the content for this DataFrame
    /// </summary>
    /// <param name="value">The content to set, must be serializable and decorated as a protobuf contract type</param>
    public void SetContent<T>(T value)
    {
        MemoryStream ms = new MemoryStream();
        Serializer.SerializeWithLengthPrefix(ms, value, PrefixStyle.Base128);
        _content = ms.GetBuffer();
        _typeName = value.GetType().AssemblyQualifiedName;
    }

    /// <summary>
    /// Encode the frame to a serialized byte array suitable for tranmission over a network
    /// </summary>
    /// <returns>The encoded byte[]</returns>
    public byte[] Encode()
    {
        DataFrame frame = (DataFrame)this;
        MemoryStream ms = new MemoryStream();
        Serializer.SerializeWithLengthPrefix(ms, frame, PrefixStyle.Base128);
        return ms.GetBuffer();
    }

    /// <summary>
    /// Factory function to create a frame from a byte array that has been received
    /// </summary>
    /// <param name="buffer">The serialized data to decode</param>
    /// <returns>A new dataframe decoded from the byte[]</returns>
    public static DataFrame Decode(byte[] buffer)
    {
        MemoryStream ms = new MemoryStream(buffer);
        DataFrame frame = Serializer.DeserializeWithLengthPrefix<DataFrame>(ms, PrefixStyle.Base128);
        frame._timeStamp = DateTime.SpecifyKind(frame._timeStamp, DateTimeKind.Utc);
        return frame;
    }
}

問題は、DataFrame を逆シリアル化できることですが、ペイロード byte[] を逆シリアル化すると、protobuf 例外が発生します。つまり、これは機能します (サーバーは UdpClient です)。

data = server.Receive(ref remoteEP);
DataFrame frame = DataFrame.Decode(data);

ただし、コンテンツが文字列であっても、protobuf 例外が発生します。

string content = frame.GetContent<string>();

誰かが私が間違っていることについての指針を持っていますか?

4

0 に答える 0