协议缓冲区ParseFromString不检查消息结尾

时间:2018-07-27 17:03:59

标签: c++ protocol-buffers

我发现了一个有趣的协议缓冲区陷阱。如果您有两条类似的消息,则可以使用C ++ API或命令行将一条消息解析为另一条消息。

limited documentation for ParseFromString并没有提到它不需要消耗所有字符串,如果没有使用它也不会失败。

我曾期望ParseFromString如果解析出类型为B的消息时无法解析类型为A的消息。毕竟,该消息包含额外的数据。然而,这种情况并非如此。 一个示例脚本演示了该问题:

#!/bin/sh

cat - >./foobar.proto <<EOF
syntax = "proto3";
package demo;
message A
{
   uint64 foo = 1;
};

enum flagx { 
  y = 0; 
  z = 1; 
}

message B {
   uint64 foolish = 1;
   flagx bar = 2;
};

EOF

cat - >./mess.B.in.txtfmt <<EOF
foolish: 10
bar: y
EOF

cat - >./mess.in.txtfmt <<EOF
foo: 10
EOF

protoc --encode=demo.A foobar.proto <./mess.A.in.txtfmt >./mess.A.proto
protoc --encode=demo.B foobar.proto <./mess.B.in.txtfmt >./mess.B.proto
protoc --decode=demo.A foobar.proto >./mess.out.txtfmt <./mess.B.proto

echo "in: "
cat mess.B.in.txtfmt
echo "out: "
cat mess.out.txtfmt

echo "xxd mess.A.proto:"
xxd mess.A.proto

echo "xxd mess.B.proto:"
xxd mess.B.proto

输出为:

in: 
foolish: 10
bar: 20
out: 
foo: 10
xxd mess.A.proto:
00000000: 080a                                    
xxd mess.B.proto:
00000000: 080a

因此,二进制消息对于消息A和消息B都是相同的。

如果您更改协议,以便使用另一个varint(uint64)代替枚举,则会得到不同的二进制消息,但是 ParseFromString仍将成功地将较长的消息解析为较短的消息。

真正令人困惑的是,它似乎也能够将较短的消息解析为较长的消息。

这是错误还是功能?

1 个答案:

答案 0 :(得分:1)

我认为这是设计使然,但文档可能会更好。

如果您尝试使用API​​而不先阅读有线格式,可能会引起混淆。接线格式与您所期望的与API无关。

接线格式强调紧凑而不是正确。如果您想检查邮件的正确性,可以使用其他方法。

您可能(可能应该或必须)在消息中包含以下一项或多项:

  • 消息类型字段
  • 消息长度字段
  • 校验和

关于能够将较短的消息解析为较长的消息的第二点是,因为在协议缓冲区3中所有字段都是可选的。 协议缓冲区2具有必填字段的概念。删除它引起了一些争议(例如,参见Why required and optional is removed in Protocol Buffers 3https://capnproto.org/faq.html#how-do-i-make-a-field-required-like-in-protocol-buffers)。 消息中不包含具有默认值(通常为0)的字段。字段名称也用数字代替。因此,两种“不同”协议的消息都可能很容易被两者解释。