protobuf是一种比json和xml等序列化工具更加轻量和高效的结构化数据存储格式
官网:https://developers.google.com/protocol-buffers/
protobuf的原理
API说明 : https://developers.google.com/protocol-buffers/docs/reference/cpp/google.protobuf.message
使用必须定义协议的模板,最后会翻译成c++的类
定义message 所有的message必须定义到一个文件中,且文件的后缀名为.proto。例如我们定义的bike.proto文件
syntax = "proto2";//用protobuf第几个协议版本
package tutorial;//包 最后生成c++的类
message mobile_request//第一条协议
{
required string mobile = 1;//等于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//第3条协议
{
required string mobile = 1; // 手机号码
required int32 amount = 2; // 充值金额
}
message recharge_response//服务端第3条协议
{
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;
}
编译message文件
编译语法:
protoc -I=$SRC_DIR --cpp_out=$DST_DIR bike.proto
SRC_DIR 表示proto文件所在的目录,cpp_out指定了生成的代码的路径,
bike.proto指proto文件名。
例如
protoc -I=./ --cpp_out=./ bike.proto
这样在当前目录生成了bike.pb.cc和bike.pb.h两个文件
设置这个变量的值就是 前面加set_
取得这条协议的值就是 直接用类名来获得
比如
set_mobile(参数);//设置第一条协议的值
string str =对象名.mobile();//取得这个协议的值 不同的参数用不同的结构来接收
编译生成的c++文件
#include<string>
#include<iostream>
using namespace std;
using namespace tutorial;//当时自定义的包
int main(void)
{
string data;//最终存储序列化的消息
//客户端发送的请求
{
mobile_request mr;//在bike.pb.h中定义的协议
mr.set_mobile("18632817488");//客户端的协议设置值
mr.SerializeToString(&data);//序列化保存到data里
/**********************************
** 通过网络把数据data发送出去 **
** **
**********************************/
}
//服务端接收数据 socket 中读取数据后
{
mobile_request mr;//在bike.pb.h中定义的协议
mr.ParseFromString(data);//序列化解析
cout<<"客户端手机号码"<<mr.mobile()<<endl;
}
return 0;
}
g++ -std=c++11 example.cc bike.pb.cc -lprotobuf//example.cc为自己写的源文件包含刚刚生成的头文件
//最后指定库即可
第二版 嵌套的协议及使用 (list_account_records_response这条协议)
#include "bike.pb.h"
#include <string>
#include <iostream>
using namespace std;
using namespace tutorial;
int main(void)
{
std::string data; //存储序列化的消息
//客户端发送请求
{
list_account_records_response larr;
larr.set_code(200);
larr.set_desc("ok");
for(int i=0; i<5; i++)
{
//增加嵌套类 嵌套类前面加add_
list_account_records_response_account_record * ar = larr.add_records();//嵌套中的类=外围类名+_内类名
ar->set_type(0);//嵌套类中设置值
ar->set_limit(i * 100);
ar->set_timestamp(time(NULL));
}
printf("recoreds size : %d\n", larr.records_size());//统计嵌套中有多少个这样的对象
larr.SerializeToString(&data);//序列化打包
//客户端发送data send(sockfd, data.c_str(), data.length());
}
//服务器端接受请求
{
list_account_records_response larr;
larr.ParseFromString(data);//序列化解压
printf("recoreds size : %d\n", larr.records_size());//获取解包后有多少个嵌套的对象
printf("code: %d\n", larr.code());//响应代号
for(int i=0; i<larr.records_size(); i++)
{
const list_account_records_response_account_record &ar = larr.records(i);//访问这个嵌套的对象 返回的是引用常量需加const
printf("limit: %d\n", ar.limit());//打印出协议中的消费金额
}
}
return 0;
}
附加
tutorial::mobile_request mr;//协议中的类形
mr.set_mobile("12345678923");
int len=mr.ByteSize();//获取这个长度
memcpy(msg,"FBEB",4);//自定义协议
*(u16*)(msg+4)=1;
*(i32)(msg+6)=len;
mr.SerializeToArray(msg+10,len);//序列化
mr.ParseFromArray(message, len);//反序列化
for(int i=0;i<len+10;i++)
{
printf("%d",msg[i]);
}