为啥协议缓冲区 C++ 库不能正确读取二进制对象
Posted
技术标签:
【中文标题】为啥协议缓冲区 C++ 库不能正确读取二进制对象【英文标题】:Why protocol buffer c++ library not reading binary objects properly为什么协议缓冲区 C++ 库不能正确读取二进制对象 【发布时间】:2011-12-09 05:04:46 【问题描述】:我使用 c++ 程序使用协议缓冲区创建了一个二进制文件。我在 C# 程序中读取二进制文件时遇到问题,因此我决定编写一个小型 c++ 程序来测试读取。
我的proto文件如下
message TradeMessage
required double timestamp = 1;
required string ric_code = 2;
required double price = 3;
required int64 size = 4;
required int64 AccumulatedVolume = 5;
写入协议缓冲区时,我先写入对象类型,然后写入对象长度和对象本身。
coded_output->WriteLittleEndian32((int) ObjectType_Trade);
coded_output->WriteLittleEndian32(trade.ByteSize());
trade.SerializeToCodedStream(coded_output);
现在,当我尝试在我的 c++ 程序中读取同一个文件时,我看到了奇怪的行为。
我的阅读代码如下:
coded_input->ReadLittleEndian32(&objtype);
coded_input->ReadLittleEndian32(&objlen);
tMsg.ParseFromCodedStream(coded_input);
cout << "Expected Size = " << objlen << endl;
cout<<" Trade message received for: "<< tMsg.ric_code() << endl;
cout << "TradeMessage Size = " << tMsg.ByteSize() << endl;
在这种情况下,我得到以下输出
Expected Size = 33
Trade message received for: .CSAP0104
TradeMessage Size = 42
当我写入文件时,我将 trade.ByteSize() 写为 33 个字节,但是当我读取同一个对象时,对象 ByteSize() 是 42 个字节,这会影响其余数据。我不确定这有什么问题。请指教。
问候, 阿洛克
【问题讨论】:
只是为了仔细检查.. 我比较了我的阅读器和编写器项目中的协议缓冲区生成的文件。生成的文件是相同的。所以我想,文件编码由于某种原因而不同。我不明白为什么它不同。 您是否有机会分享您的测试值,以便我检查哪个“更正确”? 还有 - 小点;如果你写了 ([objtype] @MarcGravell 我可以把二进制数据文件邮寄给你吗?恐怕没有其他方法可以从这里向您发送文件。我将按照您的建议对 objtype(和其他一些字段)进行更改。 @MarcGravell,刚刚给你发了一封电子邮件。 【参考方案1】:这是基于上述情况的猜测:当您使用ParseFromCodedStream
时,您实际上并没有将其限制为您之前找到的objlen
;因此,如果流包含比这更多的数据(即,这不是文件的结尾),引擎将尝试继续读取 EOF。您必须将长度限制在您的期望范围内。我不是 C++ 专家,因此无法提供直接指导,但如果这是 C#(使用 protobuf-net):
objType = ProtoReader.DirectReadLittleEndianInt32(file);
len = ProtoReader.DirectReadLittleEndianInt32(file);
// assume GetObjectType returns typeof(TradeMessage) for our objType
Type type = GetObjectType(objType);
msg = RuntimeTypeModel.Default.Deserialize(file, null, type, len, null);
【讨论】:
抱歉回复晚了。我在旅行。所以我用了你的小费,并在阅读时输入了指定的长度。后来我在文档本身中发现了这一点。这个想法是推动长度以在流中读取。 c++代码没问题。但是从我的 C# 代码中读取时我仍然遇到同样的问题 :( 使用上面的代码读取对象,它仍然停止在同一位置并出现以下错误.. ProtoBuf.ProtoException: ProtoBuf.Meta.TypeModel 消耗的字节数不正确.Deserialize(Stream source, Object value, Type type, Int32 length, SerializationContext context) 对于 c# 版本,我使用以下代码TradeMessage tMsg = Serializer.DeserializeWithLengthPrefix<TradeMessage>(stream, PrefixStyle.Fixed32);
这本来应该可以工作,但它没有工作。在此之后,我编写了一个测试 C++ 程序来检查生成的文件是否正常。 C3 版还是不行。
看起来,即使我在内部函数调用msg = RuntimeTypeModel.Default.Deserialize(file, null, type, len, null);
中指定长度作为参数,protobuf-net 也无法读取 length 字节数。这可能是什么原因? c++ 库能够正确读取对象。谢谢。【参考方案2】:
显然,我在创建二进制文件时犯了一个非常愚蠢的错误。当我将 protobuf 数据写入文件时,我没有以二进制模式打开文件,导致它在中间添加了奇怪的 ascii 字符。这导致使用 protobuf-net 库读取数据时出现问题。问题在这里得到解决。不应该花这么长时间来解决这个问题。
【讨论】:
以上是关于为啥协议缓冲区 C++ 库不能正确读取二进制对象的主要内容,如果未能解决你的问题,请参考以下文章
为啥scanf不能接收键盘输入,被跳过???改成C++的cin>>后便可以接收并继续下去。全部换成C++可以运行。