在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
。