c# - Protocol Buffer : read a message in C++ from C#

标签 c# protocol-buffers protobuf-net

我将读取从 C# 服务器发送的连续存储在 C++ 客户端套接字中的消息。我希望我可以像这样阅读消息的大小:

google::protobuf::uint32 m;
coded_input->ReadVarint32(&m);
cout << m << endl;

然后我要阅读消息:

Person person;
CodedInputStream::Limit limit = coded_input->PushLimit(m);
person.ParseFromCodedStream(coded_input);
coded_input->PopLimit(limit);

C# 代码如下所示:

var person = new Person { Id = 123456, Name = "Fred", Address = new Address { Line1 = "Flat 1", Line2 = "The Meadows ar garą " } };
Stream str = new NetworkStream(socket);
Serializer.SerializeWithLengthPrefix(str, person, PrefixStyle.Fixed32);

它不起作用。

我收到 C++ 错误(43 是 cout << m << endl; 的结果)(id、name、address 都是 Person 中的字段)

43

libprotobuf ERROR google/protobuf/message_lite.cc:123] Can't parse message of type "Person" because it is missing required fields: id, name, address

当我不读取变体并直接从 coded_input 解析消息时,一切正常(当我更改 SerializeWithLengthPrefix 以在服务器代码中序列化时)。但是我需要一种方法来区分连续的消息,所以我需要知道我要阅读的消息的大小。我只是不知道如何发送尺寸。

我试过:

Stream buf = new MemoryStream();
Serializer.Serialize(buf, person);
ProtoWriter writer = new ProtoWriter(str, null);
ProtoWriter.WriteInt32((int)buf.Length, writer);

然后我得到:

Unhandled Exception: ProtoBuf.ProtoException: Invalid serialization operation with wire-type None at position 0 at ProtoBuf.ProtoWriter.WriteInt32 (Int32 value, ProtoBuf.ProtoWriter writer) [0x00000] in :0
at protobuftest.MainClass.Main (System.String[] args) [0x00097] in /home/lorddidger/studia/csharp/protobuf-test/protobuf-test/Main.cs:31

我无法以这种方式发送任何 int。怎么了?


更新: 实际上,我的目标是找到一种使用 protobuf 传输整数(大小)的方法。我需要任何示例(C++ 和 C#)如何处理它,因为我不了解 protobuf 中的所有细节。

我注意到 SerializeWithLengthPrefix 发送的前缀 (4 uint32) 看起来像:大小 0 0 0。大小是序列化后消息的字节数(我猜)。我以为 PrefixStyle.Fixed32 说消息前只有一个 uint32 但实际上有 4 个!

最后,我认为您应该使用 ProtoWriter.WriteInt32((int)buf.Length, writer) 来传输整数,因为我遵循了 somwhere in the internet 的建议,我认为这是与 C++ 相关的解决方法。现在我知道你不应该写 varints - 它们与引擎相关,而且花时间在上面太复杂了。

我应该用 int 发送消息吗?我应该如何区分存储在第一条消息中的大小的下一条消息?

我看到 C# 可以很好地处理前缀,但是是否有任何与 C++ 和 C# 兼容的方式来声明消息的大小?否则,没有任何**ing 方法来对消息进行排队,我认为这是不合理的。

最佳答案

您的第一个示例仅包含一个长度; “带长度前缀”实际上编码为与 protobuf 兼容的流。

如果你从 C++ 解码,阅读两个 varints;第一个是字段编号和线类型;第二是长度。第一个打包为 3 位线型,其余为字段号。您也许还可以指定要跳过的字段 0 - 我不检查就想不起来了。


更新;我检查了这一点,在没有字段编号的情况下写入数据(只是一个 varint 长度前缀),然后使用两个不同的 API 在 C# 中读回它——单独使用 Deserialize,以及通过 DeserializeItems 作为可枚举 block 。我不得不修复后者中的一个错误,但以下内容将从下一次代码推送开始工作(注意:只有 reading 代码有修复,所以如果你正在编写在 C# 中和在 C++ 中阅读这不会影响你):

using (var ms = new MemoryStream())
{
    // write data with a length-prefix but no field number
    Serializer.SerializeWithLengthPrefix(ms, new Foo { Bar = 1 }, PrefixStyle.Base128, 0);
    Serializer.SerializeWithLengthPrefix(ms, new Foo { Bar = 2 }, PrefixStyle.Base128, 0);
    Serializer.SerializeWithLengthPrefix(ms, new Foo { Bar = 3 }, PrefixStyle.Base128, 0);

    ms.Position = 0;
    Assert.AreEqual(9, ms.Length, "3 lengths, 3 headers, 3 values");

    // read the length prefix and use that to limit each call
    TypeModel model = RuntimeTypeModel.Default;
    int len, fieldNumber, bytesRead;
    List<Foo> foos = new List<Foo>();
    do
    {
        len = ProtoReader.ReadLengthPrefix(ms, false, PrefixStyle.Base128, out fieldNumber, out bytesRead);
        if (bytesRead <= 0) continue;

        foos.Add((Foo)model.Deserialize(ms, null, typeof(Foo), len));

        Assert.IsTrue(foos.Count <= 3, "too much data!");
    } while (bytesRead > 0);

    Assert.AreEqual(3, foos.Count);
    Assert.AreEqual(1, foos[0].Bar);
    Assert.AreEqual(2, foos[1].Bar);
    Assert.AreEqual(3, foos[2].Bar);

    // do it using DeserializeItems
    ms.Position = 0;

    foos.Clear();
    foreach (var obj in model.DeserializeItems<Foo>(ms, PrefixStyle.Base128, 0))
    {
        foos.Add(obj);
        Assert.IsTrue(foos.Count <= 3, "too much data!");
    }
    Assert.AreEqual(3, foos.Count);
    Assert.AreEqual(1, foos[0].Bar);
    Assert.AreEqual(2, foos[1].Bar);
    Assert.AreEqual(3, foos[2].Bar);
}

关于c# - Protocol Buffer : read a message in C++ from C#,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/6230056/

相关文章:

c# - 将属性反序列化为抽象基引用

c# - 来自通用 IEnumerable<T> 的 DropDownListFor Helper

c# - Protocol Buffer ,让 C# 与 C++ 对话 : type issues and schema issues

c++ - Thrift 与 Protocol Buffer

serialization - 使用 Protocol Buffer 编码循环数据结构(例如有向图)

c# - 如何使用 protobuf-net 序列化/反序列化锯齿状/嵌套数组?

c# - 通过 protobufnet 从 Redis 反序列化大量用户定义对象时出现性能问题

c# - 网络服务响应时间增加了……怎么办?

c# - Xamarin Forms ListView 数据绑定(bind)

c# - 获取符合条件且小于 "x"的二进制数的个数