我正在实现一个客户端-服务器应用程序,并且正在研究各种序列化和传输数据的方法。我开始使用 Xml 序列化器,它工作得很好,但是生成数据很慢,并且会生成大型对象,尤其是当它们需要通过网络发送时。所以我开始研究 Protobuf 和 protobuf-net。
我的问题在于 protobuf 没有发送类型信息。使用 Xml 序列化器,我能够构建一个包装器,该包装器将通过同一流发送和接收任何各种(可序列化)对象,因为序列化为 Xml 的对象包含对象的类型名称。
ObjectSocket socket = new ObjectSocket();
socket.AddTypeHandler(typeof(string)); // Tells the socket the types
socket.AddTypeHandler(typeof(int)); // of objects we will want
socket.AddTypeHandler(typeof(bool)); // to send and receive.
socket.AddTypeHandler(typeof(Person)); // When it gets data, it looks for
socket.AddTypeHandler(typeof(Address)); // these types in the Xml, then uses
// the appropriate serializer.
socket.Connect(_host, _port);
socket.Send(new Person() { ... });
socket.Send(new Address() { ... });
...
Object o = socket.Read();
Type oType = o.GetType();
if (oType == typeof(Person))
HandlePerson(o as Person);
else if (oType == typeof(Address))
HandleAddress(o as Address);
...
我已经考虑了一些解决方案,包括创建一个主“状态”类型类,这是通过我的套接字发送的唯一类型的对象。不过,这偏离了我使用 Xml 序列化程序开发的功能,所以我想避免这个方向。
第二种选择是将 protobuf 对象包装在某种类型的包装器中,该包装器定义了对象的类型。(这个包装器还包括数据包 ID 和目的地等信息。)使用 protobuf-net 序列化一个对象,然后将该流粘贴在 Xml 标记之间似乎很愚蠢,但我已经考虑过了。有没有一种简单的方法可以从 protobuf 或 protobuf-net 中获取此功能?
我想出了第三种解决方案,并在下面发布,但如果您有更好的解决方案,请也发布!
有关字段边界错误的信息(使用 System.String
):
散列:
protected static int ComputeTypeField(Type type) // System.String
{
byte[] data = ASCIIEncoding.ASCII.GetBytes(type.FullName);
MD5CryptoServiceProvider md5 = new MD5CryptoServiceProvider();
return Math.Abs(BitConverter.ToInt32(md5.ComputeHash(data), 0));
}
序列化:
using (MemoryStream stream = new MemoryStream())
{
Serializer.NonGeneric.SerializeWithLengthPrefix
(stream, o, PrefixStyle.Base128, field); // field = 600542181
byte[] data = stream.ToArray();
_pipe.Write(data, 0, data.Length);
}
反序列化:
using (MemoryStream stream = new MemoryStream(_buffer.Peek()))
{
lock (_mapLock)
{
success = Serializer.NonGeneric.TryDeserializeWithLengthPrefix
(stream, PrefixStyle.Base128, field => _mappings[field], out o);
}
if (success)
_buffer.Clear((int)stream.Position);
else
{
int len;
if (Serializer.TryReadLengthPrefix(stream, PrefixStyle.Base128, out len))
_buffer.Clear(len);
}
}
field => _mappings[field]
扔了一KeyNotFoundException
会寻找63671269
。
如果我在散列函数中替换ToInt32
为ToInt16
,则字段值设置为29723
并且可以正常工作。如果我将System.String
' 字段明确定义为1
. 显式定义该字段与600542181
使用散列函数定义它具有相同的效果。被序列化的字符串的值不会改变结果。