【问题标题】:Google Protocol Buffers - Missing required fields even though all the fields are apparently presentGoogle 协议缓冲区 - 即使所有字段都明显存在,但缺少必填字段
【发布时间】:2012-12-18 12:58:44
【问题描述】:

我正在尝试通过 TCP 发送协议缓冲区消息,但在接收端我在尝试解析时收到“缺少必填字段”错误,即使显然所有字段都在那里。我在包含消息长度的消息之前发送一个 4 字节的标头。

这是消息定义:

message ReplayRequest {
  required string channel = 1;
  required uint32 start = 2;
  required uint32 end = 3;
}

在客户端,我正在对标头进行编码并将消息序列化为向量。

ReplayRequest req;
req.set_channel( "channel" )
req.set_start( 1 );
req.set_end( 5 );
int byte_size = req.ByteSize();
std::vector<uint8_t> write_buffer( HEADER_SIZE + byte_size );
encode_header( ... );
req.SerializeToArray( &write_buffer[HEADER_SIZE], byte_size );

这是生成缓冲区的十六进制打印,其中前 4 个字节是编码的消息长度(13 个字节)。

00 00 00 0d 0a 07 63 68 61 6e 6e 65 6c 10 01 18 05

在服务器端,我接收报头,对其进行解码,然后接收 N 个字节,其中 N 是报头中报告的消息大小。移除了标头的服务器中的缓冲区是:

0a 07 63 68 61 6e 6e 65 6c 10 01 18 05

这与一个编码的客户端减去标头完全相同,但是当我尝试 ParseFromArray 这个缓冲区时出现错误:

libprotobuf ERROR c:\umdf_runtime\protobuf-2.4.1\src\google\protobuf\message_lit
e.cc:123] Can't parse message of type "ReplayRequest" because it is missing 
required fields: channel, start, end

在调试时我注意到解码失败的地方是在 protobuf 生成的代码的这一部分:

bool ReplayRequest::IsInitialized() const {
  if ((_has_bits_[0] & 0x00000007) != 0x00000007) return false;

  return true;
}

has_bits_ 由于某种原因在服务器端被读取为零,但我不知道为什么。

有什么想法吗?

如果重要的话,我将 boost::asio 用于网络部分。

更新

根据要求,我将发布调用 parseFromArray 的代码。

request_.ParseFromArray( &data_buffer_, data_buffer_.size() );

request_ 是一个 ReplayRequest 成员变量,在此调用之前没有对其进行任何操作。

data_buffer_ 是接收 TCP 数据的向量

我确认它的大小正确为 13 字节,这是它的十六进制转储,这与我在序列化后转储缓冲区客户端时得到的相同。

0a 07 63 68 61 6e 6e 65 6c 10 01 18 05

更新 2

我能够在客户端将缓冲区解析为另一个 ReplayRequest 实例,即:

...snip...
req.SerializeToArray( &write_buffer[HEADER_SIZE], byte_size );
ReplayRequest test;
test.ParseFromArray( &write_buffer[HEADER_SIZE], byte_size );

测试已成功填充正确的字段。

【问题讨论】:

  • 如果服务器说它没有 any 的必填字段,那么我想知道您是否在服务器上错误地提取和解析了消息。您能否发布调用 ParseFromArray 的代码?
  • 我注意到您的四字节编码消息大小标头似乎是大端序...您在接收方将其正确解码为大端序吗?我猜你必须正确解码它,因为你得到了正确的 N。
  • @James 标头解码正确,可以看到我打印的服务器缓冲区正好是 13 个字节,和编​​码一样。
  • 你确定你没有在不同的程序之间使用不同的“编译”版本的原型吗?可以肯定的是,我会将 .proto 文件重新编译为必要的 c++ 文件,然后将它们直接复制到两个项目中并重新构建。同样作为测试,您是否能够在您的第一个程序中反序列化消息(使用不同的消息实例,而不是使用您序列化的同一实例)?
  • @g19fanatic 编译后的消息位于客户端和服务器链接的静态库中,因此应该是同一个。我可以反序列化为不同的实例,请查看问题的更新 2。

标签: c++ boost-asio protocol-buffers


【解决方案1】:

问题在于您传递的是指向向量的指针,而不是指向向量数据的指针。

而不是
request_.ParseFromArray( &amp;data_buffer_, data_buffer_.size() );

试试
request_.ParseFromArray( &amp;data_buffer_[0], data_buffer_.size() );

【讨论】:

  • 就是这样。我之前收到了服务器上的单个缓冲区,所以 &data_buffer_[HEADER_SIZE] 工作正常。拆分缓冲区后,我删除了访问器。谢谢。
【解决方案2】:

如果缺少必填字段但您不需要并且无法在 protofile 中将该字段更改为可选字段,则可以使用 ParsePartialFromArray 而不是 ParseFromArray。

参见 protobuf 文档:https://developers.google.com/protocol-buffers/docs/reference/cpp/google.protobuf.message_lite

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2017-02-12
    • 2014-09-13
    • 2020-12-12
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-08-02
    相关资源
    最近更新 更多