一、序列化概念回顾
二、什么是PB
将结构化数据进行序列化的一种方式
三、PB的特点
语言无关、平台无关:即PB支持Java,C++、Python等多种语言。支持多个平台
高效:即比XML更小,更快,更为简单。
扩展性、兼容性好:你可以更新数据结构,而不影响和破坏原有的旧程序。
使用特点:ProtoBuf是需要依赖通过编译生成的头文件和源文件来使用。
四、安装ProtoBuf
ProtoBuf下载地址:https://github.com/protocolbuffers/protobuf/releases
1、在windows安装
配置一下环境变量就可以了
protoc --version 打印出版本
2、在ubuntu下安装
下载依赖器
下载protoBuf
解压包 unzip
编译生成protobuf
一次执行下面的命令 大概需要半个小时左右
这里需要更改profile,修改环境变量
sudo vim /etc/profile
在里面添加下面的内容
#(动态库搜索路径) 程序加载运⾏期间查找动态链接库时指定除了系统默认路径之外的其他路径export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/usr/local/protobuf/lib/
#(静态库搜索路径) 程序编译期间查找动态链接库时指定查找共享库的路径
export LIBRARY_PATH=$LIBRARY_PATH:/usr/local/protobuf/lib/
#执⾏程序搜索路径
export PATH=$PATH:/usr/local/protobuf/bin/
#c程序头⽂件搜索路径
export C_INCLUDE_PATH=$C_INCLUDE_PATH:/usr/local/protobuf/include/
#c++程序头⽂件搜索路径
export CPLUS_INCLUDE_PATH=$CPLUS_INCLUDE_PATH:/usr/local/protobuf/include/
#pkg-config 路径
export PKG_CONFIG_PATH=/usr/local/protobuf/lib/pkgconfig/
再执行这个命令:
source /etc/profile
另外需要说一下Centos用户在安装的时候需要用到这个命令
sudo yum install autoconf automake libtool curl make gcc-c++ unzip
五、使用ProtoBuf
使用protobuf,实现一个通讯录项目,一个版本一个版本的实现,从而有效推进protobuf的学习。
快速上手
目的:完整使用PB,学习基本语法。
在快速上手中,我们会编写第一个版本的通讯录1.0。
• 对⼀个联系⼈的信息使⽤PB进⾏序列化,并将结果打印出来。
• 对序列化后的内容使⽤PB进⾏反序列,解析出联系⼈信息并打印出来。
• 联系⼈包含以下信息:姓名、年龄。
通过通讯录1.0,我们便能了解使⽤ProtoBuf初步要掌握的内容,以及体验到ProtoBuf的完整使⽤流程。
步骤一:创建.protobuf
步骤二:进行编译
-I 指定搜索目录
步骤三:
对一个联系人的信息使用PB进行序列化,并将结果打印出来。
对序列化后的内容使用PB进行反序列化,解析出联系人信息并打印出来。
六、proto3语法详解
升级版本的通讯录
• 不再打印联系⼈的序列化结果,⽽是将通讯录序列化后并写⼊⽂件中。
• 从⽂件中将通讯录解析出来,并进⾏打印。
• 新增联系⼈属性,共包括:姓名、年龄、电话信息、地址、其他联系⽅式、备注。
1、字段规则、消息类型的定义和使用
新增联系人的属性。
repeated来定义一个数组。消息中可以包含该字段任意多次(包括零次),其中重复值的顺序会被保留。可以理解为定义了⼀个数组。
singular的规则,消息中可以包含该字段零次或⼀次(不超过⼀次)。proto3语法中,字段默认使⽤该规则。
可以嵌套定义message。
引入 phone文件
定义通讯录proto文件
把通讯录的信息写入文件,首先编写makefile文件。
里面调用的函数返回的是一个已经开辟好的空间所以我们可以使用这个空间直接对contact新增联系人。
查看二进制文件
读取文件
decode的使用
2、proto3语法理解enum类型
0必须存在,必须在第一个。
也可以定义在消息体内
不能让同级的enum类型中的成员系统,多个文件中的enum类型中的类型也不能同名。会编译报错。解决方法:可以包含一个命名空间进行隔离。
通讯录2.1,更新proto文件
设置联系人电话类型
修改打印函数
枚举类型会被默认设置。反序列化时会设置枚举值为0的枚举类型。
3、Any类型的讲解
Any类型可以被理解为是一个泛型类型。可以存储任意的消息类型。
通讯录2.2版本 使用Any字段存放联系人地址。
新增联系人地址
读出联系人地址
4、oneof类型讲解
2.3版本新增其他联系方式
只能设置一个其他联系方式
字段编号不能重复,不能使用repeated来修饰里面的值,
添加联系人的其他联系方式
读取联系人的其他联系方式
5、map类型的使用
2.4版本,增加备注
不能使用repeated来修饰,设置的kv值是无序的
设置联系人备注信息
打印备注信息
6、默认值讲解
反序列化消息时,如果被反序列化的⼆进制序列中不包含某个字段,反序列化对象中相应字段时,就会设置为该字段的默认值。不同的类型对应的默认值不同:
• 对于字符串,默认值为空字符串。
• 对于字节,默认值为空字节。
• 对于布尔值,默认值为false。
• 对于数值类型,默认值为0。
• 对于枚举,默认值是第⼀个定义的枚举值,必须为0。• 对于消息字段,未设置该字段。它的取值是依赖于语⾔。• 对于设置了repeated的字段的默认值是空的(通常是相应语⾔的⼀个空列表)。
• 对于 消息字段 、 oneof字段 和 any字段 ,C++和Java语⾔中都有has_⽅法来检测当前字段是否被设置。
对于标量数据类型,在proto3语法下,没有生成has_方法。不确定是用户设置的还是一个默认值。
7、更新消息
新增字段
注意不要和老字段冲突即可
修改字段
• 禁⽌修改任何已有字段的字段编号。
• 若是移除⽼字段,要保证不再使⽤移除字段的字段编号。正确的做法是保留字段编号
(reserved),以确保该编号将不能被重复使⽤。不建议直接删除或注释掉字段。• int32,uint32,int64,uint64和bool是完全兼容的。可以从这些类型中的⼀个改为另⼀个,
⽽不破坏前后兼容性。若解析出来的数值与相应的类型不匹配,会采⽤与C++⼀致的处理⽅案(例如,若将64位整数当做32位进⾏读取,它将被截断为32位)。
• sint32和sint64相互兼容但不与其他的整型兼容。• string和bytes在合法UTF-8字节前提下也是兼容的。• bytes包含消息编码版本的情况下,嵌套消息与bytes也是兼容的。
• fixed32与sfixed32兼容,fixed64与sfixed64兼容。
• enum与int32,uint32,int64和uint64兼容(注意若值不匹配会被截断)。但要注意当反序
列化消息时会根据语⾔采⽤不同的处理⽅案:例如,未识别的proto3枚举类型会被保存在消息中,但是当消息反序列化时如何表⽰是依赖于编程语⾔的。整型字段总是会保持其的值。
• oneof:◦ 将⼀个单独的值更改为新oneof类型成员之⼀是安全和⼆进制兼容的。
◦ 若确定没有代码⼀次性设置多个值那么将多个字段移⼊⼀个新oneof类型也是可⾏的。
◦ 将任何字段移⼊已存在的oneof类型是不安全的。
删除字段
不能直接删除已存在的字段,
如果要删除老子段,要保证不使用已经被删除的或者已经被注释掉的字段编号,会导致下列问题
如何不使用已经被注释掉的字段编号呢?
reserved保留项
指定保留的字段编号
会有一个错误发生
还可以这样写
结果:我们看到年龄不在使用前面的字段编号了
上面的生日信息哪里去了呢。被存放到了未知字段中。
未知字段
未知字段是什么?
• 未知字段:解析结构良好的protocolbuffer已序列化数据中的未识别字段的表⽰⽅式。例如,当旧程序解析带有新字段的数据时,这些新字段就会成为旧程序的未知字段。
• 本来,proto3在解析消息时总是会丢弃未知字段,但在3.5版本中重新引⼊了对未知字段的保留机制。所以在3.5或更⾼版本中,未知字段在反序列化时会被保留,同时也会包含在序列化的结果中。
从哪获取未知字段?
通过Reflection类中UnKnowFields()方法获取
打印未知字段
前后兼容性
• 向前兼容:⽼模块能够正确识别新模块⽣成或发出的协议。这时新增加的“⽣⽇”属性会被当作未 知字段(pb 3.5版本及之后)。• 向后兼容:新模块也能够正确识别⽼模块⽣成或发出的协议。前后兼容的作⽤:当我们维护⼀个很庞⼤的分布式系统时,由于你⽆法同时 升级所有 模块,为了保证 在升级过程中,整个系统能够尽可能不受影响,就需要尽量保证通讯协议的“向后兼容”或“向前兼容”。
8、option选项语法详解
option选项可以影响ProtoBuf的编译属性。用的不太多,所以我们可以用文档看一下就可以了。
七、通讯录4.0实现 --- 网络版
服务器
客户端
客户端调用接口req,调用返回resp。
客户端的.proto文件和服务器的相同
客户端的菜单编写
服务端书写 add del findALL findOne接口。每个接口会有req,resp协议。
序列化req
调用接口
反序列化req 新增一个联系人,持久化存储通讯录
序列化resp
反序列化resp
AddContact方法
protobuf远远比json的序列化和反序列化效率高。json的内存占用比protobuf多一倍。