Protobuf 还常用于通讯协议、服务端数据交换场景。那么在这个示例中,我们将实现一个网络版本的通讯录,模拟实现客户端与服务端的交互,通过 Protobuf 来实现各端之间的协议序列化。
需求如下:
- 客户端可以选择对通讯录进行以下操作:
- 新增⼀个联系人
- 删除⼀个联系人
- 查询通讯录列表
- 查询⼀个联系人的详细信息
- 服务端提供增、删、查能力,并需要持久化通讯录。
- 客户端、服务端间的交互数据使用 Protobuf 来完成。

一、环境搭建
Httplib 库:cpp-httplib 是个开源的库,是一个 C++ 封装的 http 库,使用这个库可以在 Linux、Windows 平台下完成 http 客户端、http 服务端的搭建。使用起来非常方便,只需要包含头文件 httplib.h 即可。编译程序时,需要带上 -lpthread 选项。
源码库地址:
yhirose/cpp-httplib: A C++ header-only HTTP/HTTPS server and client library (github.com)
二、Centos 下编写的注意事项
如果使用 CentOS 环境,yum 源带的 g++ 最新版本是 4.8.5,发布于 2015 年,年代久远。编译该项目会出现异常,将 gcc / g++ 升级为更高版本可解决问题。
# 安装scl
yum install -y centos-release-scl
# 安装gcc 8版本
yum install -y devtoolset-8-gcc devtoolset-8-gcc-c++
# 启⽤版本
source /opt/rh/devtoolset-8/enable
# 查看版本已经变成gcc 8.3.1
gcc -v
第二步安装 gcc 8 版本的时候,如果显示如下报错:
Could not retrieve mirrorlist http://mirrorlist.centos.org/
可以参考:
SCL更换阿里数据源_centos-sclo-scl-rh.repo-CSDN博客
注意: scl 命令启用只是临时的,退出 shell 或重启就会恢复原系统 gcc 版本,如果要长期使用的话执行:
echo "source /opt/rh/devtoolset-8/enable" >> /etc/profile
三、约定双端交互接口
1、新增一个联系人
[请求]
Post /contacts/add AddContactRequest
Content-Type: application/protobuf
[响应]
AddContactResponse
Content-Type: application/protobuf
2、删除一个联系人
[请求]
Post /contacts/del DelContactRequest
Content-Type: application/protobuf
[响应]
DelContactResponse
Content-Type: application/protobuf
3、查询通讯录列表
[请求]
GET /contacts/find-all
[响应]
FindAllContactsResponse
Content-Type: application/protobuf
4、查询一个联系人的详细信息
[请求]
Post /contacts/find-one FindOneContactRequest
Content-Type: application/protobuf
[响应]
FindOneContactResponse
Content-Type: application/protobuf
四、客户端代码实现
1、add_contact.proto

2、main.cc

3、ContactException.h(定义异常类)

4、makefile

5、运行结果

五、服务端代码实现
1、add_contact.proto(服务端存储通讯录结构定义)

2、main.cc

3、makefile

4、运行结果
六、总结
1、序列化能力对比验证
分别使用 PB 与 JSON 的序列化与反序列化能力, 对值完全相同的一份结构化数据进行不同次数的性能测试。为了可读性,下面这一份文本使用 JSON 格式展示了需要被进行测试的结构化数据内容:
{
"age" : 20,
"name" : "张珊",
"phone" :
[
{
"number" : "110112119",
"type" : 0
},
{
"number" : "110112119",
"type" : 0
},
{
"number" : "110112119",
"type" : 0
},
{
"number" : "110112119",
"type" : 0
},
{
"number" : "110112119",
"type" : 0
}
],
"qq" : "95991122",
"address" :
{
"home_address" : "陕西省西安市⻓安区",
"unit_address" : "陕西省西安市雁塔区"
},
"remark" :
{
"key1" : "value1",
"key2" : "value2",
"key3" : "value3",
"key4" : "value4",
"key5" : "value5"
}
}
开始进行测试代码编写,我们在新的目录下新建 contacts.proto文件,内容如下:

使用 protoc 命令编译文件后,新建性能测试文件 compare.cc,我们分别对相同的结构化数据进行 100、1000、10000、100000 次的序列化与反序列化,分别获取其耗时与序列化后的大小。内容如下:

Makefile:

测试结果如下:
由实验结果可得:
- 编解码性能:ProtoBuf 的编码解码性能,比 JSON 高出 2-4 倍。
- 内存占用:ProtoBuf 的内存 278,而 JSON 到达 567,ProtoBuf 的内存占用只有 JSON 的 1/2。
注意:以上结论的数据只是根据该项实验得出。因为受不同的字段类型、字段个数等影响,测出的数据会有所差异。
该实验有很多可待优化的地方。但其实这种粗略的测试,也能从其中看出 ProtoBuf 的优势。
2、总结

- XML、JSON、ProtoBuf 都具有数据结构化和数据序列化的能力。
- XML、JSON 更注重数据结构化,关注可读性和语义表达能力。ProtoBuf 更注重数据序列化,关注效率、空间、速度,可读性差,语义表达能力不足,为保证极致的效率,会舍弃⼀部分元信息。
- ProtoBuf 的应用场景更为明确,XML、JSON 的应用场景更为丰富。