我将读取从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的结果&lt;&lt;&lt;&lt;&lt;&lt; endl;)(id,name,address是Person中的所有字段)
43
libprotobuf错误 谷歌/ 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 value,ProtoBuf.ProtoWriter writer) [0x00000] in:0
在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)看起来像:size 0 0 0.大小是序列化后消息的字节数(我猜)。我以为PrefixStyle.Fixed32说消息之前只有一个uint32,但是有4个!
最后,我认为你应该使用ProtoWriter.WriteInt32((int)buf.Length,writer)来传递整数,因为我遵循somwhere in the internet的建议,我认为这是与C ++相关的解决方法。现在我看到你不应该写varints - 它们与引擎有关,而且要花时间去处理它。
我应该发送带有int的消息吗?我应该如何区分下一个消息,即大小存储在第一个消息中?
我认为C#可以使用前缀,但是C ++和C#的方式是否与消息的状态大小相当?否则,没有 * *方法来排队我认为不合理的消息。
答案 0 :(得分:3)
你的第一个例子只包括一个长度; “with length prefix”实际上是在与protobuf兼容的流中编码。
如果要从c ++解码,请阅读两个 varints;第一个是场数和线型;第二个是长度。第一个打包为3位线型,其余为字段编号。您也可以指定要跳过的字段0 - 我无法在不检查的情况下回忆。
更新;我检查了这一点,编写了没有字段编号的数据(只是一个varint长度前缀),然后使用两个不同的API在C#中读取它 - 分别使用Deserialize,并通过DeserializeItems作为可枚举块。我不得不在后者中修复一个错误,但是下一个代码推送将起到以下作用(注意:只有 读取 代码有一个修复,所以如果你正在编写在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);
}