Protobuf(Protocol Buffers)是由Google开发的一种语言无关的数据序列化格式。它旨在将结构化数据(如结构化消息或文档)高效地序列化为紧凑的二进制表示,以便在网络上轻松传输或存储在磁盘上。
以下是Protocol Buffers的一些关键特性和概念:
语言无关:Protobuf不依赖于特定的编程语言,这意味着您可以使用一个简单的语言无关接口描述语言(IDL)定义数据结构,然后生成适用于各种编程语言的代码来处理这些结构。
数据结构定义:Protobuf使用结构化模式定义语言来定义要序列化的数据的结构。这个模式被编写在一个.proto文件中,它定义了数据结构的字段、类型和可选元数据。
紧凑的二进制格式:Protobuf将结构化数据序列化为二进制格式,这种格式比传统的基于文本的格式(如XML或JSON)更紧凑和高效。较小的大小减少了带宽和存储需求,使其非常适用于网络通信或存储大量数据。
高效的编码和解码:Protobuf使用高效的编码和解码机制,可以快速地对数据进行序列化和反序列化。二进制格式经过了优化,以提高速度,生成的代码提供了方便的API来处理序列化的数据。
版本控制和向后兼容性:Protobuf支持版本控制和向后兼容性。可以随着时间的推移演进数据模式,通过修改模式来适应新的需求,同时保持与旧版本数据的兼容性。
Protobuf具有许多其他功能和优点,例如可扩展性、跨平台支持和代码生成。它被广泛应用于各种领域,包括分布式系统、网络通信、数据存储和交换等。
下载ProtoBuf前一定要安装依赖库:autoconf automake libtool curl make g++ unzip
安装命令如下:
Ubuntu用户:
sudo apt-get autoconf automake libtool curl make g++ unzip -y
CentOS用户:
sudo yum install autoconf automake libtool curl make g++ unzip -y
ProtoBufe下载地址:https://github.com/protocolbuffers/protobuf/releases
复制对应版本的链接,然后在Linux中下载,我选择的是protobuf-all-21.11.zip
wget https://github.com/protocolbuffers/protobuf/releases/download/v21.11/protobuf-all-21.11.zip
下载完成之后,进行解压
unzip protobuf-all-21.11.zip
解压完成之后,会在当前文件夹生成文件protobuf-21.11,它有以下内容
进入到protobuf-21.11文件后,执行以下命令:
#第一步执行autogen.sh,但如果下载的是具体某一门语言,则不需要执行这一步
./autogen.sh
#第二步执行configure,有两种执行方式,任选其一即可,如下:
#1.protobuf默认安装在 /usr/local 目录,lib、bin都是分散的
./configure
#2.修改安装目录,同一安装在 /usr/local/protobuf 下
./configure --prefix=/usr/local/protobuf
make //执行15分钟
make check //执行15分钟
sudo make install
到此,需要回忆一下在执行configure时,如果当时选择了第一种执行方式,也就是./configure,那么到这就可以正常使用protobuf。如果选择了第二种执行方式,即修改了安装目录,那么还需要/etc/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/ocal/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/
最后一步,重新执行/etc/profile文件:
source /etc/profile
首先来一段protobuf的定义语法:
//首行:语法指定行
syntax = "proto3";
package contacts;
//定义联系人message
message PeopleInfo
{
string name = 1; //姓名
int32 age = 2; //年龄
}
syntax = “proto3”; 用于指定语法
package contacts; 相当于命名空间,防止多个message发生命名冲突
protobuf中定义一个数据结构需要用到关键字message,这点和c++中的class类似
protobuf中的标量数据类型与C++中的数据类型也是一一对应的
protobuf 数据类型 | 描述 | 打包 | C++语言映射 |
---|---|---|---|
bool | 布尔类型 | 1字节 | bool |
double | 64位浮点数 | N | double |
float | 32为浮点数 | N | float |
int32 | 32位整数 | N | int |
uin32 | 无符号32位整数 | N | unsigned int |
int64 | 64位整数 | N | __int64 |
uint64 | 64为无符号整 | N | unsigned __int64 |
sint32 | 处理负数效率更高 | N | int32 |
sing64 | 64位整数 处理负数效率更高 | N | __int64 |
fixed32 | 32位无符号整数 | 4 | unsigned int32 |
fixed64 | 64位无符号整数 | 8 | unsigned __int64 |
sfixed32 | 32位整数、能以更高的效率处理负数 | 4 | unsigned int32 |
sfixed64 | 64位整数 | 8 | unsigned __int64 |
string | 只能处理 ASCII字符 | N | std::string |
bytes | 用于处理多字节的语言字符、如中文 | N | std::string |
enum | 可以包含一个用户自定义的枚举类型uint32 | N(uint32) | enum |
message | 可以包含一个用户自定义的消息类型 | N | object of class |
N 表示打包的字节并不是固定。而是根据数据的大小或者长度。
例如int32,如果数值比较小,在0~127时,使用一个字节打包。
关于枚举的打包方式和uint32相同。
关于 fixed32 和int32的区别。fixed32的打包效率比int32的效率高,但是使用的空间一般比int32多。因此一个属于时间效率高,一个属于空间效率高。根据项目的实际情况,一般选择fixed32,如果遇到对传输数据量要求比较苛刻的环境,可以选择int32.
在消息的定义中,每个字段等号后面都有唯一的标识号,用于在反序列化过程中识别各个字段的,一旦开始使用就不能改变。
字段唯一编号的范围:[1,229-1],其中19000~19999不可用,因为在protobuf协议中对这些字段进行了保留。
值得一提的是,范围为[1,15]的字段编号需要一个字节进行编码,[16,2047]内的数字需要两个字节进行编码。编码后的字节不仅只包含了编号,还包含了字段类型。所以1~15要用来标记出现非常频繁的字段,要为将来有可能添加的、频繁出现的字段预留一些出来。
编译前面的代码
protoc --cpp_out=. contacts.proto
protoc:编译命令
–cpp_out:表示生产的是c++代码
=号后面的点:表示生成的文件放在当前目录下
contacts.proto:表示编译的文件
在上级目录进行编译
protoc -I fast_start/ --cpp_out=fast_start/ contacts.proto
-I fast_start/:表示在 fast_start目录下查找对应的文件
对于编译生成的C++代码,包含了以下内容:
注意:protobuf序列化的结果为二进制字节序列,而非文本格式
创建一个测试文件 main.cc,方法中我们实现
main.cc
#include
#include
#include "contacts.pb.h"
int main()
{
std::string people_str;
{
// 对一个联系人的信息使用PB进行序列化,并将结果打印出来
contacts::PeopleInfo people;
people.set_name("fl");
people.set_age(22);
if (!people.SerializeToString(&people_str))
{
std::cerr << "序列化联系人失败" << std::endl;
return -1;
}
std::cout << "序列化成功,结果:" << people_str << std::endl;
}
{
// 对序列化后的内容使用PB进行反序列化,解析出联系人信息并打印出来
contacts::PeopleInfo people;
if(!people.ParseFromString(people_str))
{
std::cerr << "反序列化联系人失败" << std::endl;
return -1;
}
std::cout << "反序列化成功!" << people_str << std::endl
<< "姓名: " << people.name() << std::endl
<< "年龄: " << people.age() << std::endl;
}
return 0;
}
编译和运行:
g++ -o TestPb main.cc contacts.pb.cc -std=c++11 -lprotobuf
由于ProtoBuf是把联系人对象序列化成了二进制序列,这里用string 来作为接收二进制序列的容器。所以在终端打印的时候会有换行等一些乱码显示。
所以相对于xml和JSON来说,因为被编码成二进制,破解成本增大,ProtoBuf编码是相对安全的