protocolbuffers-protobuf:从C#中读取C++中的消息
本文关键字:C++ 消息 读取 protocolbuffers-protobuf | 更新日期: 2023-09-27 17:58:30
我将读取从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、名称、地址都是Person中的字段)
43
libprotobuf错误google/protobuf/message_lite.cc:123]无法分析"Person"类型的消息因为它缺少必填字段:id、名称、地址
当我没有读取变体并从coded_input
直接解析消息时,一切都很好(当我将SerializeWithLengthPrefix
更改为在服务器代码中序列化时)。然而,我需要一种方法来区分连续的消息,所以我需要知道我要阅读的消息的大小。我只是不知道如何发送尺寸。
我试过了:
Stream buf = new MemoryStream();
Serializer.Serialize(buf, person);
ProtoWriter writer = new ProtoWriter(str, null);
ProtoWriter.WriteInt32((int)buf.Length, writer);
但后来我得到了:
未处理的异常:ProtoBuf。ProtoException:无效有线类型的序列化操作位置0处无ProtoBuf。ProtoWriter。WriteInt32(Int32值,ProtoBuf。ProtoWriter作者)[0x0000]在:0
最原始的。MainClass。主要的(System.String[]args)中的[0x0097]/home/lolddidger/studioa/csharp/protobuf test/protobuf test/Main.cs:31
我不能用那种方式发送任何int。怎么了?
更新:实际上,我的目标是找到一种用protobuf传递整数(大小)的方法。我需要任何例子(C++和C#)来处理它,因为我不了解protobuf中的所有细节。
我注意到SerializeWithLengthPrefix发送的前缀(4uint32)看起来像:size 0 0 0。大小是序列化后消息的字节数(我想)。我想是PrefixStyle。Fixed32说消息之前只有一个uint32,但有4个!
最后,我认为你应该使用ProtoWriter。WriteInt32((int)buf。Length,writer)来传递整数,因为我遵循了互联网上Somhwhere的建议,我认为这是一个与C++相关的变通方法。现在我明白了,你不应该写变量——它们与引擎有关,这太复杂了,需要花时间。
我应该发送一个带有int的消息吗?我应该如何区分大小存储在第一条消息中的下一条消息?
我看到C#可以很好地使用前缀,但有任何与C++和C#兼容的方式来声明消息的大小吗?否则,就没有**ing的方法来对消息进行排队,我会觉得这是不合理的。
您的第一个示例只包含一个长度;"带长度前缀"实际上是在protobuf兼容的流中进行编码的。
如果您是从c++解码,请读取两个变量;第一个是字段编号和导线类型;第二是长度。第一个打包为3位有线类型,其余为字段号。您还可以指定要跳过的字段0——如果不检查,我就记不起了。
更新;我检查了这一点,编写了不带字段号(只有可变长度前缀)的数据,然后在C#中使用两个不同的API将其读回——分别使用反序列化,并通过反序列化项作为可枚举块。我不得不修复后者中的一个错误,但以下内容将在下一次代码推送中起作用(注意:只有阅读的代码有修复,所以如果你用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);
}