gpt4 book ai didi

c# - 如何跨 Protobuf 发送多种类型的对象?

转载 作者:太空狗 更新时间:2023-10-29 20:06:18 27 4
gpt4 key购买 nike

我正在实现一个客户端-服务器应用程序,并且正在研究各种序列化和传输数据的方法。我开始使用 Xml Serializers,它工作得很好,但生成数据很慢,并且生成大对象,尤其是当它们需要通过网络发送时。所以我开始研究 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] 在查找 63671269 时抛出 KeyNotFoundException

如果我在散列函数中将 ToInt32 替换为 ToInt16,字段值将设置为 29723 并且可以正常工作。如果我将 System.String 的字段显式定义为 1,它也会起作用。将字段显式定义为 600542181 与使用哈希函数定义它具有相同的效果。被序列化的字符串的值不会改变结果。

最佳答案

这个功能实际上是内置的,尽管不是很明显。

在这种情况下,预计您将为每种消息类型指定一个唯一编号。您使用的重载将它们全部作为“字段 1”传递,但是有一个重载允许您包含此额外的 header 信息(尽管调用代码仍然需要决定如何将数字映射到类型)。然后您可以指定不同的类型,因为不同的字段是流(注意:这仅适用于 base-128 前缀样式)。

我需要仔细检查,但目的是像下面这样的东西应该起作用:

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using ProtoBuf;
static class Program
{
static void Main()
{
using (MemoryStream ms = new MemoryStream())
{
WriteNext(ms, 123);
WriteNext(ms, new Person { Name = "Fred" });
WriteNext(ms, "abc");

ms.Position = 0;

while (ReadNext(ms)) { }
}
}
// *** you need some mechanism to map types to fields
static readonly IDictionary<int, Type> typeLookup = new Dictionary<int, Type>
{
{1, typeof(int)}, {2, typeof(Person)}, {3, typeof(string)}
};
static void WriteNext(Stream stream, object obj) {
Type type = obj.GetType();
int field = typeLookup.Single(pair => pair.Value == type).Key;
Serializer.NonGeneric.SerializeWithLengthPrefix(stream, obj, PrefixStyle.Base128, field);
}
static bool ReadNext(Stream stream)
{
object obj;
if (Serializer.NonGeneric.TryDeserializeWithLengthPrefix(stream, PrefixStyle.Base128, field => typeLookup[field], out obj))
{
Console.WriteLine(obj);
return true;
}
return false;
}
}
[ProtoContract] class Person {
[ProtoMember(1)]public string Name { get; set; }
public override string ToString() { return "Person: " + Name; }
}

请注意,此目前无法在 v2 版本中运行(因为“WithLengthPrefix”代码不完整),但我会在 v1 上对其进行测试。如果有效,我会将上述所有场景添加到测试套件中,以确保它确实在 v2 中有效。

编辑:

是的,它在“v1”上运行良好,输出:

123
Person: Fred
abc

关于c# - 如何跨 Protobuf 发送多种类型的对象?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/3048650/

27 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com