【protobuf(1)】首次理解与实践
Protobuf是Protocol Buffers的简称,它是Google公司开发的一种数据描述语言,是一种轻便高效的结构化数据存储格式,可以用于结构化数据串行化,或者说序列化 。它很适合做数据存储或 RPC 数据交换格式。可用于通讯协议、数据存储等领域的语言无关、平台无关、可扩展的序列化结构数据格式。目前提供了 C++、Java、Python 三种语言的 API。
- protobuf是类似与json一样的数据描述语言(数据格式)
- protobuf非常适合于RPC数据交换格式
注意:protobuf本身并不是和gRPC绑定的。它也可以被用于非RPC场景,如存储等
protobuf的优劣势
1)优势:
- 序列化后体积相比Json和XML很小,适合网络传输
- 序列化反序列化速度很快,快于Json的处理速度
- 消息格式升级和兼容性还不错
- 支持跨平台多语言
2)劣势:
- 应用不够广(相比xml和json)
- 二进制格式导致可读性差
- 缺乏自描述
如何实现序列化?
主流的序列化和反序列化工具有:XML、JSON、ProtoBuf;
ProtoBuf工作特点
通过Protobuf编译器,可以将.proto后缀的数据结构文件编译成各编程语言专用的类文件,简化了序列化和反序列化的过程。
- 我们使用ProtoBuf的编译器来编译这个.proto文件,就会得到一个.cpp和.hpp的文件
- 在我们的工作代码中包含以下ProtoBuf编译出来的.cpp和.hpp文件就可以了
Protobuf与DDS的关系
在分布式系统中,Protobuf和DDS经常结合使用,以优化数据传输效率和可靠性。具体来说,它们之间的关系体现在以下几个方面:
- 数据序列化:Protobuf用于定义和序列化数据,生成紧凑的二进制格式。这种格式的数据可以高效地通过网络传输,并减少带宽占用。
- DDS通信:DDS作为中间件,负责在分布式节点之间传输数据。通过DDS的发布-订阅模型,节点可以灵活地订阅和发布数据,而无需关心数据的具体传输过程。
- 集成使用:在ROS2(Robot Operating System 2)等分布式机器人操作系统中,Protobuf和DDS被紧密集成。Protobuf用于定义消息结构,而DDS则负责这些消息的传输。通过编写适配器或自定义middleware层,可以将Protobuf消息转换为DDS可理解的格式,并在DDS网络中传输。
快速上手
在快速上⼿中,将实现:
• 对⼀个联系⼈的信息使⽤ProtoBuf进⾏序列化,并将结果打印出来.
• 对序列化后的内容使⽤ProtoBuf进⾏反序列,解析出联系⼈信息并打印出来。
步骤1 创建proto文件
- 创建一个.proto文件:
这里我们创建一个PeopleInfo.proto文件,需要完成:
- 初始化工作,比如:指定PB语法版本、为当前.proto文件中的数据指定作用域; 当前.proto文件所采用的语法版本为"proto3"的版本,默认采用proto2语法进行编译,但是 proto2语法支持上没有proto3广。
- 定义的类声明一个命名空间,来避免不同 .proto之间的命名冲突的问题; 这个命名空间的定义并不是强制的,编译过后会变为C++中的namespace 命名空间。
- 定义类了,在.proto文件中定义类是利用message 关键字来完成的,在message 定义的类中,我们只需要定义出该类所包含的属性即可,同时我们需要给这些属性进行编号,并且每个同级属性之间不能重复,这是PB语法要求的;
同时编号也不是随便乱设的,编号也是有范围的,编号只能从1 ~ 2^29 -1之间进行取数,其中19000 ~ 19999不可取,因为这些编号已经被PB官方征用了!使用了编译就会出错; 在字段命名上,我们也是要注意一点规范,比如使用全小写字母、多个字母之间使用下划线连接;
PB中那些标量类型,与C++中的那些类型相对应:
.proto Type | Notes | C++ Type |
---|---|---|
double | null | double |
float | null | float |
int32 | 使用变成编码。负数的编码效率比较低(若字段出现负数的频率比较高,可以考虑使用sint32代替) | int32 |
int64 | 使用变长编码。负数的编码效率比较低(若字段出现负数的频率比较高,可以考虑使用sint64代替) | int64 |
uint32 | 使用变长编码 | uint32 |
uint64 | 使用变长编码 | uint64 |
sint32 | 使用变长编码。符号整型。负数编码效率高于常规int32 | int32 |
sint64 | 使用变长编码。符号整型。负数编码效率高于常规int64 | int64 |
fixed32 | 定长4字节。若值常大于2^28则会比uint32更高效 | uint32 |
fixed64 | 定长8字节。若常值大于2^28则会比uint64更高效 | uint64 |
sfixed32 | 定长4字节 | int32 |
sfixed64 | 定长8字节 | int64 |
bool | null | bool |
string | 包含UTF-8和ASCII编码的字符串,长度不能超过2^32 | string |
bytes | 可以包含任意的字节序列但长度不能超过2^32 | string |
步骤2:编译PeopleInfo.proto文件形成对应的C++文件;
protoc [-I] --xxx_out=OUT_DIR file.proto
-I :表示指定被编译的.proto文件所在目录,可多次指定,用空格隔开;若不指定则默认在当前路径下进行搜索;
–xxx_out: 将.proto文件编译成那种编程语言的文件;eg:--cpp_out将.proto文件编译成C++文件、 --java_out=将.proto文件编译成java文件、 --csharp_out=将.proto文件编译成C#文件等
OUT_DIR: 编译完成过后的文件所存放的路径;
举例:
protoc -I test1/ --cpp_out=./test1/ PeopleInfo.proto
/在test1目录下检索PeopleInfo.proto文件,然后将其编译成C++文件,将该C++文件放在test1/目录下;(指定检索目录的情况)
步骤3:在c++ 中引用
我们的目的是将一个联系人的信息进行序列化,然后打印序列化结果,然后再反序列化,打印出反序列化的结果。编译代码记得链接 protobuf
库。
参考
https://blog.csdn.net/Gefangenes/article/details/131319610
https://blog.csdn.net/qq_62106937/article/details/134095333