Protobuf---消息的更新
·
增加message的成员对象
注意事项
- 不要和老字段冲突
举个例子
message old
{
int age = 1;
}
-------------------------------------------------------------------------------------------
message new
{
int age = 1;
string name = 1;//这样是不被允许的
}
修改message
- 禁止修改已有字段的编号,因为这样会引发很多未知的问题,比如存储在二进制文件中某个对象的值的编号为2,现在你将message对应该内容的对象的编号更改为其他,当你在将该消息进行反序列化的时候,存储在二进制文件中的该内容还是2,因为你对message的更改,致使二进制文件中的存储的那个对象无法反序列化成功,因为它找不到对应的编号对象了
- 移除老字段,要保证不在使用该字段的编号了,原因同上,正确的做法是使用reserved关键字,保证该编号不能被重复使用
- int32,int64,uin32,uint64和bool是相互兼容的,在message中将一个类型更改为另一个类型在进行反序列化是没有影响的
- sint32和sint64是相互兼容的,但不与其它类型兼容
- string和bytes在合法的UTF-8编码级下是相互兼容的
- bytes包含消息编码版本的情况下,嵌套消息与bytes也是相互兼容的
- fixed32与sfixed32兼容,fixed64与sfixed64兼容
- enum与int32,uint32,int64,uint64兼容
- oneof:
- 将一个单独的值更改为新oneof类型成员之一是安全和二进制兼容的
- 若确定没有代码一次性设置多个值,那么将多个字段移入新oneof类型也是可行的
- 将任何字段移入已存在的oneof类型是不安全的
删除message内部成员
- 如果要删除老字段,要保证不使用已经被删除的或已经被注释掉的字段编号
reserved的使用
- reserved 字段标号,字段标号,……
- reserved 字段标号 to 字段标号
- reserved "对象名称", "对象名称",……
举个例子
//联系人
message PeopleInfo
{
reserved 2,10,11,100 to 200;
reserved "age";
string name = 1;
int32 birthday = 3
message Phone
{
string number = 1;//电话号码
}
repeated Phone phone = 4;
}
//通讯录
messge Contacts
{
repeated PeopleInfo contacts = 1;
}
未知字段
位置字段是什么?
- 未知字段:解析结构良好的protocol buffer已经序列化数据中的未识别字段的表达方式。例如,当旧message解析带有新字段的数据时,这些新字段就会成为就程序的未知字段
- 以前proto3在解析消息遇到未知字段就会丢弃,但在3.5版本中重新引入对未知字段的保留机制,所以在3.5或更高的版本中,未知字段在反序列化时会被保留,同时也会包含在序列化的结果当中
从哪获取?

从自定义类型继承的Message中我们可以看到两个函数,分别为GetDescriptor和GetReflection,调用前者可以得到一个google::protobuf::Descriptor类型对象的地址,该类型是描述message定义相关内容,使用该指针对象可以得到自定义的message的名字,还可以获取在message中定义的字段描述信息,后者,可以得到一个google::protobuf::Reflection类型对象的地址,通过该指针对象调用GetUnknownFields()函数获取google::protobuf::UnknowFieldSet类型对象的地址,该对象装有全部的未知字段,通过该指针对象可以调用field(int)函数可以拿到某一个未知字段,该函数传入整形,该整形值为未知字段的下标,通过filed_count()接口可以拿到未知字段一共有多少个(下面有在使用,不明白的话,看一眼就懂了),目的是可以拿到这个序号对应的未知字段,可以到google::protobuf::UnknowField类型对象的地址,该指针对象,该位置对象的所有信息
举个例子
void PrintContacts(const Contacts& contact)
{
for(int i = 0;i<contacts.contacts_size();i++)
{
const PeopleInfo& people = contacts.contacts(i);
std::cout<<"-------------联系人"<<i+1<<"-------------"<<std::endl;
std::cout<<"联系人姓名:"<<people.name()<<std::endl;
std::cout<<"联系人年龄:"<<people.age()<<std::endl;
int j = 1;
for(const PeopleInfo_Phone& phone : people.phone())
{
std::cout<<"联系人电话:"<<phone.number()<<std::endl;
}
}
const Reflection* reflection = PeopleInfo::GetReflection();
const UnknowFieldSet& set = reflection->GetUnknownFields(people);//people的含义是,将内容反序列化到people对象,所产生的未知字段
for(int j = 0;j<set.field_count();j++)
{
const UnkonwnFieldSet& unknow_field = set.field(j);
std::cout<<"未知字段"<<j+1<<": "<<"编号:"<<unknow_field.number();
switch(unknow_field.type())
{
case UnknowField::Type::TYPE_VARINT:
std::cout<<"值:"<<unknow_field.varint()<<std::endl;
case UnknowField::Type::TYPE_VARTYPE_LENTH_DELIMITED:
std::cout<<"值:"<<unknow_field.=length_delimited()<<std::endl;
}
}
}
int main()
{
Contacts contacts;
fstream input("../contacts.bin",ios::in | ios::binary);
if(!contacts.ParseFromIstream(&input))
{
std::cerr<<"Failed to parse contacts."<<std::endl;
input.close();
return -1;
}
return 0;
}
前后兼容性
向前兼容:老模块能够正确识别新模块生成或者发出的协议
向后兼容:新模块也能正确识别老模块生成或者发出的协议
option选项
optimize_for:该选项为文件选项,可以设置protoc编译器优化级别,分别为:SPEED,CODE_SIZE,LITE_RUNTIME。受选项影响,设置不同优化级别,编译.proto文件后生成的代码内容是不同的
- SPEED:protoc编译器将生成的代码是高度优化的,代码运行效率高,但是生成代码后会占用更多空间。SPEED是默认选项
- CODE_SIZE:proto编译器将生成最小的类,会占用更小的空间,是依赖基于反射代码(上面的Reflection类)来实现的序列化,反序列化各种操作。但和SPEED恰恰相反,它的代码运行效率较低。这种方式适合用在包含大量的.proto文件,但不盲目追求速度的应用中
- LITE_RUNTINE:生成代码的执行效率高,同时编译后占用空间也是非常小的。这是牺牲Protocol Buffer提供的反射功能为代价的,所以只能进行序列化,反序列化操作,因为没有继承google::protobuf::Message而是直接继承了google::protobuf::MessageLite,所以就没有可以拿到位置字段的能力和拿到message相关定义的能力,所以我们在链接BP库时仅需要libprotobuf-lite,而非libprotobuf
- allow_alias:允许使用相同的常量值分配给不同的枚举常量,用来定义别名,该选项为枚举选项
举个例子
//对optimize_for进行举例
syntax = "proto3";
option optimize_for = LITE_RUNTIME;
message PeopleInfo
{
string name = 1;
}
//对allow_alias进行举例
enum PhoneType
{
opention allow_alias = true;//允许对一个枚举对象取别名,就是可以对不同的枚举对象赋相同的值
MP = 0;
TEL = 1;
LANDLINE = 1;//若不加opention allow_alias = true;这一行会报错,不设置true也会报错
}
更多推荐




所有评论(0)