C++ protobuf
Protocol Buffers(简称 protobuf)是由 Google 开发的一种轻量级、跨平台的数据序列化框架,类似于 JSON 或 XML,但更加高效。它是一种语言无关、平台无关的机制,能够用于定义结构化数据并对其进行序列化和反序列化。
如果需要 高性能 和 高效的数据传输,protobuf 是一个理想选择。
它非常适合需要 跨平台通信 和 维护复杂数据结构 的场景,比如微服务、分布式系统和移动开发。
一、protobuf
1.protobuf 的主要特点
- 高效:
- 二进制格式,占用空间小,解析速度快。
- 比 JSON 和 XML 更加高效,适合大规模数据传输。
- 跨平台、跨语言:
- 支持多种编程语言(如 C++、Java、Python、Go、JavaScript 等)。
- 序列化的数据可以在不同平台和语言之间无缝传输。
- 结构化数据:
- 使用
.proto
文件定义数据结构,便于维护和管理。
- 使用
- 易扩展:
- 支持向后兼容(backward compatibility)和向前兼容(forward compatibility)。
2.protobuf 的核心概念
-
消息(Message):
-
类似于类的定义,用于描述数据结构。例如:
proto message Person { int32 id = 1; // 唯一标识符 string name = 2; // 姓名 string email = 3; // 邮箱 }
-
-
字段(Field):
- 每个字段都有一个类型、名称和唯一的标识符(标签号),标签号用于标识字段。
-
序列化与反序列化:
- 序列化:将消息转换为二进制格式(用于存储或传输)。
- 反序列化:将二进制数据解析为可用的消息对象。
-
.proto 文件:
- 用于定义数据结构,开发者根据此文件生成代码。
- 支持版本控制和扩展。
3.protobuf 的使用场景
- 网络通信:
- 用于服务间的数据交换,特别是在微服务架构中。
- 比如 gRPC 就使用 protobuf 作为默认数据格式。
- 数据存储:
- 用于持久化数据,例如配置文件和数据库记录。
- 日志系统:
- 高效存储和分析日志数据。
- 跨语言数据交互:
- 在不同语言的系统间传递数据。
4.protobuf 与其他数据格式的对比
特点 | protobuf | JSON | XML |
---|---|---|---|
效率 | 高,二进制序列化 | 中等,文本格式 | 低,冗余较多 |
可读性 | 差,二进制不可读 | 好,文本易读 | 好,文本易读 |
大小 | 小,紧凑的二进制表示 | 大,键值对冗余 | 大,标签冗余 |
兼容性 | 良好,支持向后兼容 | 较差,字段删除需慎重 | 较差,字段删除需慎重 |
语言支持 | 多种(C++、Java等) | 广泛(所有语言) | 广泛(所有语言) |
5.protobuf 的工作流程
- 编写
.proto
文件定义消息结构。 - 使用
protoc
工具生成特定语言的代码。 - 在代码中调用生成的类进行序列化和反序列化。
二、C++使用protobuf
1. 安装protobuf库
确保你安装了protobuf的编译器和C++库:
-
下载并安装protobuf:
-
从 GitHub protobuf releases 下载源码。
-
解压安装,验证安装:
bash protoc --version
-
-
如果你使用包管理工具:
- Ubuntu:
sudo apt install protobuf-compiler libprotobuf-dev
- macOS:
brew install protobuf
- Ubuntu:
2. 定义 .proto 文件
创建一个.proto
文件,用于定义你的消息结构。例如:
proto
syntax = "proto3";
message Person {
int32 id = 1;
string name = 2;
string email = 3;
}
保存为 person.proto
。
3. 生成C++代码
使用 protoc
生成C++文件:
bash
protoc --cpp_out=. person.proto
运行后,会生成两个文件:
person.pb.h
person.pb.cc
4. 在项目中集成protobuf
将生成的 .h
和 .cc
文件加入你的C++项目。确保你的编译器能够找到protobuf库。
- 示例代码:
cpp
#include <iostream>
#include "person.pb.h"
int main() {
// 创建一个 Person 对象
Person person;
person.set_id(1);
person.set_name("John Doe");
person.set_email("john.doe@example.com");
// 序列化为字符串
std::string serialized_data;
if (!person.SerializeToString(&serialized_data)) {
std::cerr << "Failed to serialize data." << std::endl;
return -1;
}
// 反序列化为对象
Person deserialized_person;
if (!deserialized_person.ParseFromString(serialized_data)) {
std::cerr << "Failed to parse data." << std::endl;
return -1;
}
// 输出解析的结果
std::cout << "ID: " << deserialized_person.id() << std::endl;
std::cout << "Name: " << deserialized_person.name() << std::endl;
std::cout << "Email: " << deserialized_person.email() << std::endl;
return 0;
}
5. 编译项目
编译时,链接protobuf库:
bash
g++ -o main main.cpp person.pb.cc -lprotobuf -std=c++17
6. 运行程序
执行生成的二进制文件:
bash
./main
7. 调试和优化
- 如果序列化的内容需要与文件或网络交互,可以用
SerializeToOstream
和ParseFromIstream
。 - 使用
MessageLite
版本可以减少依赖。