在protobuf-c中,可选的uint32变量的值为0

Message Data{ optional uint64 userid = 1; repeated string usernames = 2; optional uint32 status = 3; } 

案例1 – ‘status’包含非零值’

如果status =非零值当我使用data__pack(const Data *消息,uint8_t * out)函数打包数据时,’out’的长度为100,函数返回100(值100用作例)。

案例2 – ‘status’包含值0

如果status = 0(对于与用例1中相同的userid和username的值)当我使用data__pack()函数打包数据时,’out’的长度为99但函数仍然返回100。

如果您注意到上述两种情况,如果变量’status’使用值0,则如果’status’包含非零值,则输出缓冲区的长度始终小于长度1。 我已将has_status变量设置为1,因此这似乎不是问题。

这是一个protobuf错误还是我做错了什么?

其他细节:我使用的Protobuf版本 – protobuf-c-0.15

关于这个问题的附加细节

 Data msg = DATA__INIT; void* buf = (void*) NULL; int buf_len = 0; buf_len = data__get_packed_size(&msg); buf = (void*) malloc(buf_len+1); data__pack(&msg, buf); 

我使用data__get_packed_size()来获取大小。 在通过HTTP请求发送到预期的接收器时,我发送内容长度为’buf_len’,并且’buf’用于HTTP主体。

 Content-Length: 100 Content-Type: application/protobuf  

接收器现在继续等待直到它收到完整的请求(即100)并且连接最终超时。 在这种情况下,接收器如何知道如何处理这个,因为它只会等待Content-Length为100?

uint32编码需要两个varint,每个varint至少有一个字节长。 第一个字节包含编码(“wire”)类型和标签号,如果标签最多为15,则长度为一个字节。第二个varint是整数本身,如果整数是at,则长度为一个字节省略(“默认”)可选字段根本不占用任何字节; 省略的字段不以任何方式编码在线上; 他们的缺席必须推断出来。

因此,如果省略了可选的uint32字段,则线编码将比包含可选字段的相同protobuf短至少两个字节。

你没有说明你如何计算“出’的长度”。 唯一正确的方法是在打包data对象之前调用data__get_packed_size 。 但是,我猜你正在使用strlen(out) ,它不会产生正确的结果。 strlen只能用于字符串 ; 更具体地说,它只能用于NUL-terminated strings ,因为它在遇到NUL (0)字节时停止计数,如果没有看到NUL字节则产生未定义的行为。 您不得在任意二进制数据上使用strlen

在protobuf编码中,标记为3且值为0的显式uint32将编码为0x18 0x00 ,而标记为3且值为42的显式uint32将编码为0x18 0x2A 。 如果数据编码中没有早期的NUL,那么编码为0的第二个字节将终止strlen的计数。 在某些理想的情况下(比如,缓冲区足够长,并且在将消息打包到其中之前被清除到所有NUL ), strlen将报告protobuf的长度为0作为比protobuf短一个“字符” 42 ,因为0的编码中的NUL发生在消息的末尾,导致strlen提前停止计数一个字节。

这是一种解释,而不是一种解决方法。 不要在非NUL端接字符串的东西上使用strlen