ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

protobuf

2022-06-14 15:33:46  阅读:179  来源: 互联网

标签:protobuf proto Person person message 序列化 id


protobuf 简介

protobuf (protocol buffer) 是谷歌内部的混合语言数据标准。通过将结构化的数据进行序列化(串行化),用于通讯协议、数据存储等领域的语言无关、平台无关、可扩展的序列化结构数据格式。

序列化:将数据结构或对象转换成二进制串的过程。反序列化:将在序列化过程中所产生的二进制串转换成数据结构或对象的过程。

message 介绍

messageprotobuf中定义一个消息类型是通过关键字message字段指定的,这个关键字类似于C++/Java中的class关键字。使用protobuf编译器将proto编译成C++代码之后,每个message都会生成一个名字与之对应的C++类,该类公开继承自google::protobuf::Message

message 消息定义

创建tutorial.person.proto文件,文件内容如下:

// FileName: tutorial.person.proto 
// 通常文件名建议命名格式为 包名.消息名.proto 

// 表示正在使用proto2命令
syntax = "proto2"; 

//包声明,tutorial 也可以声明为二级类型。
//例如a.b,表示a类别下b子类别
package tutorial; 

//编译器将生成一个名为person的类
//类的字段信息包括姓名name,编号id,邮箱email,
//以及电话号码phones
message Person { 
  required string name = 1;  // (位置1)
  required int32 id = 2;  
  optional string email = 3;  // (位置2)

  enum PhoneType {  //电话类型枚举值 
    MOBILE = 0;  //手机号  
    HOME = 1;    //家庭联系电话
    WORK = 2;    //工作联系电话
  } 
  
  //电话号码phone消息体
  //组成包括号码number、电话类型 type
  message PhoneNumber {
    required string number = 1;    
    optional PhoneType type = 2 [default = HOME]; // (位置3)
  }  
  
  repeated PhoneNumber phones = 4; // (位置4)
} 


// 通讯录消息体,包括一个Person类的people
message AddressBook { 
  repeated Person people = 1; 
}

包声明

proto 文件以package声明开头,这有助于防止不同项目之间命名冲突。在C++中,以package声明的文件内容生成的类将放在与包名匹配的namespace中,上面的.proto文件中所有的声明都属于tutorial

字段规则

  • required:消息体中必填字段,不设置会导致编解码异常。(例如位置1)

  • optional: 消息体中可选字段,可通过default关键字设置默认值。(例如位置2)

  • repeated: 消息体中可重复字段,重复的值的顺序会被保留(例如位置3)。其中,proto3默认使用packed方式存储,这样编码方式比较节省内存。

  • 标识号:在消息体的定义中,每个字段都必须要有一个唯一的标识号,标识号是[0,2^29-1]范围内的一个整数。以Person为例,name=1,id=2, email=3, phones=4 中的1-4就是标识号。

许多标准的简单数据类型都可以用作message字段类型,包括bool,int32,float,doublestring。还可以使用其他message类型作为字段类型在消息体中添加更多结构。在上面的示例中,Person包含PhoneNumber message, 而AddressBook包含Person message。甚至可以定义嵌套在其他message中的message类型。例如,上面的PhoneNumber定义在Person

函数方法

message关键字声明的的消息体,允许你检查、操作、读、或写整个消息,包括解析二进制字符串,以及序列化二进制字符串。除此之外,也定义了下列方法:

Person:缺省的构造函数。

~Person():缺省的析构函数。

Person(const Person& other):拷贝构造函数。

Person& operator=(const Person& other):
赋值 (Assignment )操作符。

const UnknownFieldSet& unknown_fields() const:
返回当解析信息时遇到的未知字段的集合。

UnknownFieldSet* mutable_unknown_fields():
返回当前解析信息时遇到的未知字段的集合的一个mutale指针。

编译proto文件

可以执行以下protoc命令对.proto文件进行编译,生成对应的c文件。Linux系统通过 help protoc 查看protoc命令的使用详解。

protoc -I=$SRC_DIR 
--cpp_out=$DST_DIR  xxx.proto
  • $SRC_DIR:proto 所在的源目录
  • --cpp_out 生成C++代码
  • $DST_DIR 生成代码的目标目录
  • xxx.proto:要针对哪个proto文件生成接口,例如 tutorial.person.proto

编译完成后,将生成2个文件 tutorial.pb.htutorial.pb.c 其中tutorial表示包名,pb是protobuf的缩写。

此外,protocol buffer 编译器为.proto文件中定义的消息的每个字段生成一套存取器方法

对于 message Person 中的 required int32 id = 2,编译器将生成下列存取器方法:

bool has_id() const: 用于判断字段id是否存在。如果字段被设置,返回true。

int32 id() const: 返回字段id的当前值,如果字段没有被设置,返回缺省值。

void set_id(int32 value) : 设置字段id的值。调用此方法后,has_id() 将返回true以及id() 将返回value

void clear_id():清除字段的值。调用此方法后,has_id()将返回false 以及id()将返回缺省值。

当然,对于其他类型的字段,编译器也会生成不同的存取方法,这里就不一一列举了。

使用message

//获取person实例:

tutorial::AddressBook addressBook;
Person *person = addressBook.add_people(); 

//写入一个message:

person->set_id(id);
getline(cin,*person->mutable_name());

//读取一个message: 

person.id();
person.name();
person.email(); 

protobuf的优点

性能方面

  • 序列化后,数据大小可缩小3倍
  • 序列化速度快
  • 传输速度快

使用方面

  • 使用简单:proto编译器自动进行序列化和反序列化
  • 维护成本低:多平台只需要维护一套对象协议文件,即.proto文件
  • 可扩展性好:不必破坏旧的数据格式,就能对数据结构进行更新
  • 加密性好:http传输内容抓包只能抓到字节数据

使用范围

  • 跨平台、跨语言、可扩展性强
原文链接:https://juejin.cn/post/7029961388411846664

标签:protobuf,proto,Person,person,message,序列化,id
来源: https://www.cnblogs.com/sqdtss/p/16374843.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有