I have a transport application which is used in asa a pub-/sub server to relay data between clients. The pub-/sub server need only know a little about each piece of data, for example it needs the topicname to be able to relay a published topic to the correct subscribers.
To achieve this I have thought out a scheme in which a class that is decorated as a ProtoContract includes a byte[] which in turn contains protobuf-net serialized data. This way the server does only have to deserialize a very small part of the data it relays (and does not need to know types). My type looks like this;
[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;
}
}
Problem is, that I am able to deserialized a DataFrame, but when deserializing the payload byte[] I get protobuf exceptions. That is, this works (server is a UdpClient);
data = server.Receive(ref remoteEP);
DataFrame frame = DataFrame.Decode(data);
But this will give me a protobuf exception, even if the content is a string;
string content = frame.GetContent<string>();
Does anyone have any pointers on what I am doing wrong?