protobuf 是一种比 json 和 xml 等序列化工具更加轻量和高效的结构化数据存储格式。
https://developers.google.com/protocol-buffers/
安装
apt-get install autoconf automake libtool curl make g++ unzip
$ git clone https://github.com/protocolbuffers/protobuf.git
$ cd protobuf
$ git submodule update --init --recursive
$ ./autogen.sh
$ ./configure
$ make
$ make check
$ sudo make install
$ sudo ldconfig
遇到问题:
sudo chmod +777 -R protobuf-3.19.4
需求设计
1.定义proto文件
syntax = "proto2";
package tutorial;
message mobile_request
{
required string mobile = 1;
}
message mobile_response
{
required int32 code = 1; //响应代号
required int32 icode = 2; //验证码
optional string data = 3; //失败原因
}
message login_request
{
required string mobile = 1; // 手机号码
required int32 icode = 2; // 验证码
}
message login_response
{
required int32 code = 1; // 响应代号
optional string desc = 2; // 验证码
}
message recharge_request
{
required string mobile = 1; // 手机号码
required int32 amount = 2; // 充值金额
}
message recharge_response
{
required int32 code = 1; // 响应代号
optional string desc = 2; // 验证码
required int32 balance = 3; // 最新的余额
}
message account_balance_request
{
required string mobile = 1;
}
message account_balance_response
{
required int32 code = 1; // 响应代号
optional string desc = 2; // 验证码
required int32 balance = 3;
}
message list_account_records_request
{
required string mobile = 1;
}
message list_account_records_response
{
required int32 code = 1; // 响应代号
optional string desc = 2; // 验证码
message account_record
{
required int32 type = 1; // 0 : 骑行消费, 1 : 充值, 2 : 退款
required int32 limit = 2; // 消费或者充值金额
required uint64 timestamp = 3; // 记录发生时的时间戳
}
repeated account_record records = 3;
}
message list_travel_records_request
{
required string mobile = 1;
}
message list_travel_records_response
{
required int32 code = 1; // 响应代号
optional string desc = 2; // 验证码
message travel_record
{
required uint64 stm = 1; // start timestamp
required uint32 duration = 2; // 骑行时长
required uint32 amount = 3; // 所耗金额
}
required double mileage = 3; // 里程
required double discharge = 4; // 排放
required double calorie = 5; // 卡路里
repeated travel_record records = 6;
}
package
:为.proto文件添加package声明符,可以防止不同 .proto项目间消息类型的命名发生冲突。
protobuf 包类型的解析和C++类似,都是由内而外进行解析。对于C++,产生的类会被包装在C++的命名空间中(tutorial)
option
:会影响特定环境下的处理方式,但是不会改变整个文件声明的含义。
message
:消息体,包含了多个fields(数据项),每一个fields都是key-value类型。message经过protoc编译后会生成对应的class类,field则会生成对应的方法。
2.protoc编译器
使用proto文件定义好结构数据后,可以使用protoc编译器生成结构数据的源代码,这些源代码提供了读写结构数据的接口,从而能够构造、初始化、读取、序列化、反序列化结构数据。
使用以下命令生成相应的接口代码:
// $SRC_DIR: .proto所在的源目录
// --cpp_out: 生成C++代码
// $DST_DIR: 生成代码的目标目录
// xxx.proto: 要针对哪个proto文件生成接口代码
protoc -I=$SRC_DIR --cpp_out=$DST_DIR $SRC_DIR/xxx.proto
编译完成后将会生成一个xxx.pb.h和xxx.pb.cpp文件,会提供类似SerializeToOstream()、set_name()、name()等方法。
3.调用接口进行序列化、反序列化
#include "bike.pb.h"
#include <string>
#include <iostream>
using namespace std;
using namespace tutorial;
int main(void)
{
std::string data; //存储序列化的消息
//客户端发送请求
{
mobile_request mr;
mr.set_mobile("18684518289");
mr.SerializeToString(&data);
cout<<"序列化后的数据["<<data.length()<<"]: "<< data << endl;
cout<<hex<<(int)*((char*)data.c_str())<<endl;
cout<<hex<<(int)*((char*)data.c_str() + 1)<<endl;
//客户端发送data send(sockfd, data.c_str(), data.length());
}
//服务器端接受请求
{
//receive(sockfd, data, ...);
mobile_request mr;
mr.ParseFromString(data);
cout<<"客户端手机号码: " << mr.mobile() << endl;
}
return 0;
}
压缩效率高:服务器间的海量数据传输与通信,可以节省磁盘和带宽,protobuf 适合处理大数据集中的单个小消息,但并不适合处理单个的大消息。
解析速度快:可以提高服务器的吞吐能力。
项目应用
class MobileCodeReqEv : public iEvent //手机验证码请求
{
public:
MobileCodeReqEv(const std::string& mobile) :iEvent(EEVENTID_GET_MOBLIE_CODE_REQ, iEvent::generateSeqNo()) // 显式调用基类构造函数
{
msg_.set_mobile(mobile);
};
const std::string& get_mobile() { return msg_.mobile(); }; // 手机号码
virtual std::ostream& dump(std::ostream& out) const; // 将对象信息输出到 std::ostream 流
virtual i32 ByteSize() { return msg_.ByteSize(); }; // 返回 msg_ 对象的字节大小
virtual bool SerializeToArray(char* buf, int len) { return msg_.SerializeToArray(buf, len); }; // 将 msg_ 对象序列化到一个字符数组中
private:
tutorial::mobile_request msg_; //用于存储和操作手机验证码请求的相关信息。
};