1.syntax = "proto2";选择 proto2 或者 proto3 的语法,这里指定了 proto2 的语法
结构化数据被称为 Message
- package haomo.pos; 包名。在 C++ 里表现为 namespace,避免命名冲突
包名用于组织类和接口,以便更好地管理和组织代码。haomo 是包的顶级名称,pos 是子包的名称。
3.message 是消息体,它就是一个结构体/类.
字段格式:限定修饰符① | 数据类型② | 字段名称③ | = | 字段编码值④ | [字段默认值⑤]
包含name和type。
- Scalar Value Types
fixed32的打包效率比int32的效率高,但是使用的空间一般比int32多。因此一个属于时间效率高,一个属于空间效率高。根据项目的实际情况,一般选择fixed32,如果遇到对传输数据量要求比较苛刻的环境,可以选择int32.
| Protobuf Type | 说明 | C++ Type | Java Type | Python Type[2] | Go Type |
|---|---|---|---|---|---|
| float | 固定4个字节 | float | float | float | float32 |
| double | 固定8个字节 | double | double | float | float64 |
| int32 | varint编码 | int32 | int | int | int32 |
| uint32 | varint编码 | uint32 | int | int/long | uint32 |
| uint64 | varint编码 | uint64 | long | int/long | uint64 |
| sint32 | zigzag 和 varint编码 | int32 | int | int | int32 |
| sint64 | zigzag 和 varint编码 | int64 | long | int/long | int64 |
| fixed32 | 固定4个字节 | uint32 | int | int | uint32 |
| fixed64 | 固定8个字节 | uint64 | long | int/long | uint64 |
| sfixed32 | 固定4个字节 | int32 | int | int | int32 |
| sfixed64 | 固定8个字节 | int64 | long | int/long | int64 |
| bool | 固定一个字节 | bool | boolean | bool | bool |
| string | Lenth-Delimited | uint64 | String | str/unicode | string |
| bytes | Lenth-Delimited | string | ByteString | str | []byte |
| bytes | Lenth-Delimited | string | ByteString | str | []byte |
- Specifying Field Types
type可以自定义(specify enumerations and composite types like other message types for your field.)
4.Assigning Field Numbers
为每个字段分配一个介于1 - 536,870,911的数字:
- 给定的数字必须在该消息的所有字段中是唯一的。
- 19,000 - 19,999被保留给Protocol Buffers实现。如果在消息中使用这些保留的字段号,Protocol Buffers编译器会报错。
- 你不能使用先前保留的字段号或已分配给扩展的字段号。
- 一旦消息类型在使用中,就不能更改字段号,因为它标识了消息的字段在消息的二进制格式中的位置。"更改"字段号相当于删除该字段,并创建一个具有相同类型但具有新编号的新字段。
最常设置的字段使用 1 - 15 的字段编号。较低的字段编号值在线格式中占用更少的空间。
5.Deleting Fields(谨慎)
不能删除required fields;
如果不再需要 non-required field, 并且所有客户端代码中的引用都已删除时,可删除该字段。必须保留该字段编号(reserve the deleted field number),防止以后再使用该字段;保留该字段名称(reserve the field name),确保JSON和FormatText实例的消息类型继续正确解析。
6.Reserved Fields
message Foo {
reserved 2, 15, 9 to 11;
reserved "foo", "bar";
}
(9 to 11 is the same as 9, 10, 11).
7.Adding More Message Types
建议在每个.proto文件里包含较少的Message Types(message, enum, and service))
8.Specifying Field Labels
- optional字段有两种状态:
1. 字段被设置,并且包含一个显式设置或从线路上解析的值。它将被序列化到线路上。
2. 字段未设置,并且将返回默认值。它不会被序列化到线路上。
可选相对于发送方,在发送消息时,可以有选择性的设置或者不设置该字段的值。对于接收方,如果能够识别可选字段就进行相应的处理,如果无法识别,则忽略该字段,消息中的其它字段正常处理。---因为optional字段的特性,很多接口在升级版本中都把后来添加的字段都统一的设置为optional字段,这样老的版本无需升级程序也可以正常的与新的软件进行通信,只不过新的字段无法识别而已,因为并不是每个节点都需要新的功能,因此可以做到按需升级和平滑过渡。
- repeated字段类型可以在一个良好格式的消息中重复零次或多次。重复值的顺序将被保留。可以看作是在传递一个数组的值。其实protobuf处理这个字段的时候,也是optional字段一样,另外加了一个count计数变量,用于标明这个字段有多少个,这样发送方发送的时候,同时发送了count计数变量和这个字段的起始地址,接收方在接受到数据之后,按照count来解析对应的数据即可。
- map配对的键/值字段类型。特别的缩短版的repteated。
message 消息名 {
map<key, value> name = n;
}
message Test6 {
map<string, int32> g = 7;
}
等同于
message Test6 {
message g_Entry {
optional string key = 1;
optional int32 value = 2;
}
repeated g_Entry g = 7;
}
- required 字段尽量不要使用。被从 proto3 中删除。required 字段表示是一个必须字段,必须相对于发送方,在发送消息之前必须设置该字段的值,对于接收方,必须能够识别该字段的意思。发送之前没有设置required字段或者无法识别required字段都会引发编解码异常,导致消息被丢弃。
repeated 可以将重复的基本类型字段(任何不是字符串或字节的标量类型)声明为“packed”。在proto2中,可以使用字段选项[packed=true]来实现。在 proto3 里是default. 相反,它们被编码为一个包含每个元素连接在一起的单个 LEN 记录。要解码,元素从 LEN 记录中逐个解码,直到有效负载用完。下一个元素的开始由前一个元素的长度决定,而前一个元素的长度又取决于字段的类型。
message Test5 {
repeated int32 f = 6 [packed=true];
}
只有基本数值类型的重复字段可以声明为“packed”。这些类型通常使用VARINT、I32或I64的编码类型。
重要提示: 如前所述,不应该在新字段中使用required关键字。必需字段的语义应该在应用层面实现。现有的必需字段应该被视为消息定义的永久、不可变的元素。将字段从required更改为optional几乎是不可能的。如果存在任何过时的读取器,它将认为没有该字段的消息是不完整的,并且可能会拒绝或丢弃这些消息。 另一个与required字段相关的问题出现在某人向枚举中添加一个值时。在这种情况下,未识别的枚举值被视为缺失,这也导致了必需值检查失败。