安装
1 | # 下载pb |
使用
pb命令
命令 | 描述 |
---|---|
-h |
帮助手册 |
-IPATH,--proto_path=PATH |
指定proto目录,可以指定多个 |
1 | protoc -I=源地址 --xxx_out=目标地址 xxx/xxx.proto |
语法
- pack1/test1.proto
1 | syntax = "proto3"; // (1) |
- pack1/test2.proto
1 | syntax = "proto3"; |
- pack2/test3.proto
1 | syntax = "proto3"; |
(1):指明我们使用的是proto3语法,若不指定该行 protocol buffer 编译器会认为是 proto2 。该行必须是文件的第一个非空或非注释行。
(2):指定包名
(3):定义枚举
(4):定义结构体
(5):定义普通变量
(6):定义数组
(7):引入同目录的包
(8):定义同目录包的结构体
(9):引入不同目录的包
(10):定义不同目录的包的结构体
编译命令
1 | protoc -I=D:\test_protoc --java_out=D:\test_protoc pack1/test1.proto |
字段编号
息中定义的每个字段都有一个唯一编号。字段编号用于在消息二进制格式中标识字段,同时要求消息一旦使用字段编号就不应该改变。注意一点 1 到 15 的字段编号需要用 1 个字节来编码,编码同时包括字段编号和字段类型。16 到 2047 的字段变化使用 2 个字节。因此应将 1 到 15 的编号用在消息的常用字段上。注意应该为将来可能添加的常用字段预留字段编号。
关键字
关键字 | 描述 |
---|---|
singular | 默认值,非数组 |
repeated | 数组 |
reserved | 在采取彻底删除或注释掉某个字段的方式来更新消息类型时,将来其他用户再更新该消息类型时可能会重用这个字段编号。后面再加载该 .ptoto 的旧版本时会引发好多问题,例如数据损坏,隐私漏洞等。一个防止该问题发生的办法是将删除字段的编号(或字段名称,字段名称会导致在 JSON 序列化时产生问题)设置为保留项 reserved。protocol buffer 编译器在用户使用这些保留字段时会发出警告。(注意,不能在同一条 reserved 语句中同时使用字段编号和名称。) |
在 proto3 中,标量数值类型的重复字段默认会使用 packed 压缩编码。
注释
使用 C/C++ 风格的 //
和 /* ... */
语法在 .proto 文件添加注释。
语言支持
当 protocol buffer 编译器作用于一个 .proto 文件时,编辑器会生成基于所选编程语言的关于 .proto 文件中描述消息类型的相关代码 ,包括对字段值的获取和设置,序列化消息用于输出流,和从输入流解析消息。
- 对于 C++, 编辑器会针对于每个 .proto 文件生成.h 和 .cc 文件,对于每个消息类型会生成一个类。
- 对于 Java, 编译器会生成一个 .java 文件和每个消息类型对应的类,同时包含一个特定的 Builder类用于构建消息实例。
- Python 有些不同 – Python 编译器会对于 .proto 文件中每个消息类型生成一个带有静态描述符的模块,以便于在运行时使用 metaclass 来创建必要的 Python 数据访问类。
- 对于 Go, 编译器会生成带有每种消息类型的特定数据类型的定义在.pb.go 文件中。
- 对于 Ruby,编译器会生成带有消息类型的 Ruby 模块的 .rb 文件。
- 对于Objective-C,编辑器会针对于每个 .proto 文件生成pbobjc.h 和 pbobjc.m. 文件,对于每个消息类型会生成一个类。
- 对于 C#,编辑器会针对于每个 .proto 文件生成.cs 文件,对于每个消息类型会生成一个类。
- 对于 Dart,编辑器会针对于每个 .proto 文件生成.pb.dart 文件,对于每个消息类型会生成一个类。
数据类型
.proto Type | Notes | C++ Type | Java Type | Python Type[2] | Go Type | Ruby Type | C# Type | PHP Type | Dart Type |
---|---|---|---|---|---|---|---|---|---|
double | double | double | float | float64 | Float | double | float | double | |
float | float | float | float | float32 | Float | float | float | double | |
int32 | 使用变长编码。负数的编码效率较低——若字段可能为负值,应使用 sint32 代替。 | int32 | int | int | int32 | Fixnum or Bignum (as required) | int | integer | int |
int64 | 使用变长编码。负数的编码效率较低——若字段可能为负值,应使用 sint64 代替。 | int64 | long | int/long[3] | int64 | Bignum | long | integer/string[5] | Int64 |
uint32 | 使用变长编码。 | uint32 | int[1] | int/long[3] | uint32 | Fixnum or Bignum (as required) | uint | integer | int |
uint64 | 使用变长编码。 | uint64 | long[1] | int/long[3] | uint64 | Bignum | ulong | integer/string[5] | Int64 |
sint32 | 使用变长编码。符号整型。负值的编码效率高于常规的 int32 类型。 | int32 | int | int | int32 | Fixnum or Bignum (as required) | int | integer | int |
sint64 | 使用变长编码。符号整型。负值的编码效率高于常规的 int64 类型。 | int64 | long | int/long[3] | int64 | Bignum | long | integer/string[5] | Int64 |
fixed32 | 定长 4 字节。若值常大于2^28 则会比 uint32 更高效。 | uint32 | int[1] | int/long[3] | uint32 | Fixnum or Bignum (as required) | uint | integer | int |
fixed64 | 定长 8 字节。若值常大于2^56 则会比 uint64 更高效。 | uint64 | long[1] | int/long[3] | uint64 | Bignum | ulong | integer/string[5] | Int64 |
sfixed32 | 定长 4 字节。 | int32 | int | int | int32 | Fixnum or Bignum (as required) | int | integer | int |
sfixed64 | 定长 8 字节。 | int64 | long | int/long[3] | int64 | Bignum | long | integer/string[5] | Int64 |
bool | bool | boolean | bool | bool | TrueClass/FalseClass | bool | boolean | bool | |
string | 包含 UTF-8 和 ASCII 编码的字符串,长度不能超过 2^32 。 | string | String | str/unicode[4] | string | String (UTF-8) | string | string | String |
bytes | 可包含任意的字节序列但长度不能超过 2^32 。 | string | ByteString | str | []byte | String (ASCII-8BIT) | ByteString | string | List |
enum | 枚举,必须有一个 0 值,才可以作为数值类型的默认值。0 值常量必须作为第一个元素 |
默认值
当解析消息时,若消息编码中没有包含某个元素,则相应的会使用该字段的默认值。默认值依据类型而不同:
- 字符串类型,空字符串
- 字节类型,空字节
- 布尔类型,false
- 数值类型,0
- 枚举类型,第一个枚举元素
- 内嵌消息类型,依赖于所使用的编程语言。
对于可重复类型字段的默认值是空的( 通常是相应语言的一个空列表 )。
注意一下标量字段,在消息被解析后是不能区分字段是使用默认值(例如一个布尔型字段是否被设置为 false )赋值还是被设置为某个值的。例如你不能通过对布尔值等于 false 的判断来执行一个不希望在默认情况下执行的行为。同时还要注意若一个标量字段设置为默认的值,那么是不会被序列化以用于传输的。
链接