首先看下下面这个proto文件,我们后面的proto基本用法都是基于这个proto进行讲解
package pkgName;
option java_package = "test1.test2";
option java_outer_classname = "TestClass";
message mmData {
optional int32 num = 1;
optional int32 def_num = 2 [default=10];
required string str = 3;
repeated string rep_str = 4;
}
1、包名package
proto文件使用关键字package指定当前包名,它类似于java中的包名或者C++中的命名空间,主要是用来防止不同消息类型的命名冲突。使用protobuf编译器将proto文件编译成C++代码之后,当前proto文件中的所有声明都将位于命名空间pkgName::下。
2、option
在消息定义之前,可以通过option来进行配置,常用的两个option:
option java_package=“xxx/xxx” 该选项指定了java文件生成的路径
option java_outer_classname=“xxx” 该选项制定了生成的java类名
3、消息类型
3.1 message
Protobuf中定义一个消息类型是通过关键字message字段指定的,这个关键字类似于C++/Java中的class关键字,使用protobuf编译器将proto编译成C++代码之后,每个message都会生成一个名字与之对应的C++类。
如上面的message 将生成一个名字为mmData的类,该类公开的继承google::protobuf::Message。
3.2 字段规则
message中的字段规则有三种。
required : 字段属性为必填字段。若不设置,则会导致编解码异常,导致消息被丢弃。
optional : 字段属性为可选字段。发送方可以选择性根据需要进行设置;对于optional属性的字段,可以通过default关键字为字段设置默认值,即当发送方没有对该字段进行设置的时候,将使用默认值。如果没有对字段设置默认值,就会根据特定的类型给字段赋予特定的默认值。对于bool类型,默认值为false;对于string类型,默认值为空字符串;对于数值类型,默认值为0;对于枚举类型,默认值是枚举类型中的第一个值。
repeated : 字段属性为可重复字段,该字段可以包含[0,n]个元素,字段中的元素顺序被保留。
注意:
(1)在proto3版本中,字段规则上移除了required,并把optional字段改名为singular。所有没有指定字段规则的字段默认为optional,对于为什么删除了require规则,参考:为什么 proto3 移除了 required 和 optional?
(2)在proto2版本中,默认配置下,一个optional没有被设置或者被显示的设置为默认值,在序列化二进制格式的时候,这个字段将会被去掉,导致反序列化之后,无法区分当初没有设置还是设置了默认值,即使使用hasXXX()方法,对于设置的默认值的字段,也是返回false。解决方法:区分 Protobuf 中缺失值和默认值
3.3 标识号
在消息体的定义中,每个字段都必须要有一个唯一的标识号。这些标识号是用来在消息的二进制格式中识别各个字段的,一旦使用就不能再改变,否则会导致原有消息编解码出现异常。
标识号是[0,2^29 - 1]范围内的一个整数,其中[19000,19999)之间的标识号在protobuf协议的实现中被预留了,所以特写注意不要使用这个范围内的标识号,若使用进行编译的时候也会告警.
Field numbers 19000 through 19999 are reserved for the protocol buffer library implementation.
1
注意:[1,15]内的标识号在编码的时候占用一个字节,[16,2047]之内的标识符占用两个字节,所以尽量为频繁使用的字段分配[1,15]内的标识号,另外预留出来一部分给未来可能频繁使用的字段。
3.4 数据类型
3.4.1 基本数据类型
消息体中的每个字段都必须指定字段类型,可选的字段类型和语=与其对应的C++/Java中的类型如下图:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-KTrq0uIB-1588870734147)(/Users/wingwei/workspace/wx_workspace/文档/image/proto数据类型.png)]
图片资源来源:https://blog.csdn.net/m15927408113/article/details/79976528
3.4.2 枚举类型
字段类型除了上述基本的字段类型之外,也可以是枚举类型。protobuf中的枚举类型使用方法与C++中的枚举类型类似,在将proto文件编译成C++代码后,其对应的类型也是C++中的枚举类型。
package pkgName;
// 定义枚举类型
enum DayName {
Sun = 0;
Mon = 1;
Tues = 2;
Wed = 3;
Thur = 4;
Fri = 5;
Sat = 6;
}
message workDay {
// 消息类型使用枚举类型
optional DayName day = 1;
}
枚举常量的值必须在32位整数范围内,因为enum值是使用可编码方式存储的,对负数存储不够高效,因此不推荐在enum中使用负数。
枚举类型可以定义在message内,也可以定义在message外,若定义在message内,其他message要使用则需要通过messageType.enumType来进行引用。
默认情况下,枚举类型中的字段值不可重复,但是通过对enum添加option allow_alias = true;来达到对同一个枚举值起一个别名的目的,若不添加allow_alise并且有重复的枚举值编译的时候会报错。
package pkgName;
enum DayName {
// 若不添加该option,会报错:
// "pkgName.Test" uses the same enum value as "pkgName.Sat". If this is intended, set 'option allow_alias = true;' to the enum definition.
option allow_alias = true;
Sun = 0;
Mon = 1;
Tues = 2;
Wed = 3;
Thur = 4;
Fri = 5;
Sat = 6;
Test = 6; // Test与Sat字段值重名
}
3.4.3 map数据类型
除了上述类型之外,message还支持map<Type,Type>类型。
package pkgName;
message Tdata {
map<int32,string> data = 1;
}
注意:
(1) protobuf中的map实质上是unordered_map
(2) proto中map类型不能用optional/required/repeated任何类型修饰。
3.4.4 message类型
protobuf允许将其他消息类型用作字段类型。如下面userData中存在一个workDay类型的数据。
package pkgName;
message workDay {
optional int day = 1;
}
message userData {
optional workDay userDays = 1;
}
3.4.5 嵌套消息类型
与C++中的类可以嵌套类似,消息也可以嵌套。即允许你在一个messageType中定义另一个messageType,然后使用它,
package pkgName;
message OutterData {
// 嵌套消息定义,在生成的C++代码中,该message被组织为类:outterData_Tdata
message Tdata {
optional int32 a = 1;
}
// 引用嵌套消息
optional Tdata data = 1;
}
message嵌套在生成C++代码之后,实际上内部类生成的类名为OutterData_Tdata
4、import导入其他proto文件
4.1 import
我们可以通过import导入其他proto文件,并使用该proto文件中的定义的消息类型。与c++中的include或者Java中的import类似。
如:
// a.proto文件
package Ap;
message A{
optional int32 a = 1;
}
// b.proto文件
import "a.proto"; // 导入a.proto文件,这样就可以直接使用a.proto中定义的消息类型了。
package Bp;
message B{
optional Ap.A a = 1;
}
4.2 import public
默认情况下,proto只允许引用直接import的文件中定义的数据类型。如b.proto中导入了a.proto,c.proto中导入了b.proto;默认情况下,c.proto中只能引用b.proto中定义的数据类型,而引用不到a.proto中的数据类型。若c.proto要使用a.proto中定义的数据类型,则b.proto引用a.proto的时候要使用import public。
// a.proto文件
package Ap;
message A{
optional int32 a = 1;
}
// b.proto文件,使用import public 允许a.proto对b.proto的引用者(c.proto)可见
import public "a.proto";
package Bp;
message B{
optional Ap.A a = 1;
}
// c.proto
import "b.proto";
package Cp;
message C{
optional Ap.A a = 1;
}
这种用法在迁移proto文件到新的位置的时候十分有用,如Message类要从old.proto迁移到new.proto文件中,这个时候如果要在不修改对old.proto的文件的情况下,直接将Message移动到new.proto中,然后在old.proto中import public new.proto即可。
5、更新Message消息类型原则
为了达到前后消息类型兼容的目的,扩展Message消息类型的时候需要注意一下几点:
1、不要更改任何已有的字段的数值标识。
2、所添加的字段属性必须是optional 或者repeated类型,如果扩展required类型,会导致旧的消息解析异常
3、非required字段可以移除。要保证它们的标示在新的消息类型中不再使用
4、一个非required的字段可以转换为一个扩展,反之亦然——只要它的类型和标识号保持不变。
5、int32, uint32, int64, uint64,和bool是全部兼容的,这意味着可以将这些类型中的一个转换为另外一个,而不会破坏向前、 向后的兼容性。如果解析出来的数字与对应的类型不相符,那么结果就像在C++中对它进行了强制类型转换一样(例如,如果把一个64位数字当作int32来 读取,那么它就会被截断为32位的数字)。
6、sint32和sint64是互相兼容的,但是它们与其他整数类型不兼容。
7、string和bytes是兼容的——只要bytes是有效的UTF-8编码。
8、嵌套消息与bytes是兼容的——只要bytes包含该消息的一个编码过的版本。
9、fixed32与sfixed32是兼容的,fixed64与sfixed64是兼容的。
6、protobuf扩展
6.1 extensions&extend
protobuf允许Message中预留出一个标识号段用作给第三方扩展使用。当其他人需要在message中扩展新的字段的时候,就不需要直接修改原文件,直接在自己的proto文件中定义该Message的扩展字段即可。(注意:一定要保证不同文件中扩展的标识号不能重复,否则会导致数据不一致的现象)
// base.proto
package base;
message BaseProto{
optional int32 id = 1;
extensions 1000 to 2000;
}
// 扩展BaseProto
extend base.BaseProto{
optional string name = 1000;
}
注意访问扩展字段的方式与访问普通字段的方式有所不同,如在C++中对扩展字段设置为:
base::BaseProto test;
test.SetExtension(name, "wing");
同时还提供了一些其他的访问操作,如:HasExtension(),ClearExtension(),GetExtension(),MutableExtension(),以及 AddExtension()等。
6.2 嵌套扩展
嵌套扩展即为可以在另外的类中添加扩展。
// base.proto
package base;
message BaseProto{
optional int32 id = 1;
extensions 1000 to 2000;
}
message OtherProto {
extend BaseProto {
optional string name = 1000;
}
}
嵌套扩展在c++中的访问方式类似,即在访问扩展字段的时候在字段前加上作用域空间。
base::BaseProto test;
test.SetExtension(OtherProto::name, "wing");
参考链接:
protobuf语法详解
Protobuf3语法详解
为什么 proto3 移除了 required 和 optional?
[译]Protobuf 语法指南
基于protobuf的RPC实现
————————————————
版权声明:本文为CSDN博主「langzi989」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/u014630623/article/details/105985944/