概述
protobuf也叫protocol buffer,是google 的一种数据交换的格式,它跨语言、跨平台。可以实现多种语言文件的数据传输实现(java、c#、c++、go 和 python 等),如一个cpp程序和一个python程序的数据传输。
由于它是一种二进制的格式,比使用 xml 、json进行数据交换快许多。所以它的效率和兼容性都很优秀,可以把它用于分布式应用之间的数据通信。
编译与安装
通过百度网盘分享的文件:proto
链接:https://pan.baidu.com/s/1o7Vmcyofz6r_phLdh-A2lA?pwd=t172
提取码:t172
解压后运行shell脚本即可
编写简单的proto文件
proto文件
// 声明protobuf的版本
syntax = "proto3";
// 声明代码所在的包, 相当他cpp的namespace
package fixbug;
// 定义下面选项, 才可以生成service服务类和rpc方法描述, 默认不生成
option cc_generic_services = true;
message ResultCode {
int32 errcode = 1;
bytes errmsg = 2;
bool success = 3;
}
// 定义登录消息类型(结构体)
message LoginRequest {
// 1 表示数据的序号
bytes name = 1; // bytes = string
bytes pwd = 2;
}
// 定义登录响应消息类型
message LoginResponse {
ResultCode result = 1;
bool success = 2;
}
message GetFriendListsRequest { uint32 userid = 1; }
message User {
bytes name = 1;
uint32 age = 2;
enum Sex {
MAN = 0;
WOMAN = 1;
}
Sex sex = 3;
}
message GetFriendListsResponse {
ResultCode result = 1;
bool success = 2;
// 存储列表
repeated User friend_list = 3;
}
// 在protobuf里面定义描述rpc方法的类型 -service
service UserServiceRpc {
rpc Login(LoginRequest) returns (LoginResponse);
rpc GetFriendList(GetFriendListsRequest) returns (GetFriendListsResponse);
}
在终端中执行
protoc test.proto --cpp_out=./
test.proto是你自定义的proto文件
然后会得到test.pb.h、test.pb.cc文件
proto自定义数据结构与.pb.h中函数的关系
test.pb.h初看有些复杂,但其实都是有规律的
1.常规变量类型
这里的常规变量,指的是string、int等类型,而不是自定义的数据结构
// 定义登录消息类型(结构体)
message LoginRequest {
// 1 表示数据的序号
bytes name = 1; // bytes = string
bytes pwd = 2;
}
转为pb.cc文件后,主要分为两种读写函数:
set_变量名()
:如set_name(),往name变量中写数据变量名()
:如name(),只读访问name变量
2.自定义变量类型
这里的常规变量,指的是string、int等类型,而不是自定义的数据结构
message GetFriendListsResponse {
ResultCode result = 1;
bool success = 2;
// 存储列表
repeated User friend_list = 3;
}
转为 pb.cc 文件后,主要分为两种读写函数:
mutable_变量名()
:如ResultCode* mutable_result(),获得变量result的指针,之后就可读可写了变量名()
:如name(),只读访问name变量
注意对于 repeated User friend_list
变量,还会有:
使用 repeated
关键字定义的字段在 Protobuf 序列化和反序列化时会被当作一个集合或数组来处理。这个 User friend_list
可以作为一个动态数组来使用。
- add_变量名():如
User* add_friend_list()
,它返回的是要新增元素地址
3.service服务
在proto中我们会定义远程调用的服务函数
service UserServiceRpc {
rpc Login(LoginRequest) returns (LoginResponse);
rpc GetFriendList(GetFriendListsRequest) returns (GetFriendListsResponse);
}
- 转为pb.cc文件后,会从 ”结构体“名 (这里是
UserServiceRpc
)继承,得到 ”结构体“名_Stub 这个类(这里是UserServiceRpc_Stub
) UserServiceRpc_Stub
的构造函数是由一个RpcChannel* channel
作为参数的传入的- 而 proto 文件中定义的
Login
、GetFriendList
函数,本质上是调用CallMethod
函数(channel_->CallMethod(descriptor()->method(0),controller, request, response, done)
; - 再追溯一下,
RpcChannel
是一个类,里面是CallMethod
函数是虚函数,这说明RpcChannel
类以及它的CallMethod
函数,都需要我们开发者进行重写
序列化与反序列化
-
序列化是将数据结构或对象转换为可以存储或传输的格式(通常是字节序列/string类型)的过程。
其目的是将复杂的数据结构转化为一种便于存储、传输或在不同系统之间交换的形式。
-
反序列化则是序列化的逆过程,它将序列化后的数据(如字节流)重新转换回原始的数据结构或对象。
假设我们有一个包含学生姓名、年龄和成绩的对象。序列化可能会将这个对象转换为一个特定格式的字符串,如
{“name”:“张三”,“age”:20,“score”:90}
。而反序列化就是把接收到的这样的字符串重新转换回包含姓名、年龄和成绩的学生对象。
【protobuf】ProtoBuf——序列化概念、序列化和反序列化、为什么需要序列化和反序列化、如何实现序列化、ProtoBuf 是什么、ProtoBuf 的使用特点-CSDN博客
#include "test.pb.h"
#include <iostream>
#include <string>
using namespace fixbug;
int main()
{
// 初始化变量, 封装了login请求对象的数据
LoginRequest req;
req.set_name("zhang san");
req.set_pwd("123456");
// 对象数据序列化, => string
std::string send_str;
if(req.SerializeToString(&send_str))
{
std::cout << send_str << std::endl;
}
// 从send_str中反序列化一个login请求对象
LoginRequest reqB;
if(reqB.ParseFromString(send_str))
{
std::cout << reqB.name() << std::endl;
std::cout << reqB.pwd() << std::endl;
}
return 0;
}