当前位置:网站首页>protobuf的基本用法
protobuf的基本用法
2022-07-26 09:50:00 【_索伦】
protobuf的安装配置
protobuf(protocol buffer)是google 的一种数据交换的格式,它独立于平台语言。
google 提供了protobuf多种语言的实现:java、c#、c++、go 和 python,每一种实现都包含了相应语言的编译器以及库文件。
由于它是一种二进制的格式,比使用 xml(20倍) 、json(10倍)进行数据交换快许多。可以把它用于分布式应用之间的数据通信或者异构环境下的数据交换。作为一种效率和兼容性都很优秀的二进制数据传输格式,可以用于诸如网络传输、配置文件、数据存储等诸多领域。
在github源代码下载地址:https://github.com/google/protobuf
1、解压压缩包:unzip protobuf-master.zip
2、进入解压后的文件夹:cd protobuf-master
3、安装所需工具:sudo apt-get install autoconf automake libtool curl make g++ unzip
4、自动生成configure配置文件:./autogen.sh
5、配置环境:./configure
6、编译源代码(时间比较长):make
7、安装:sudo make install
8、刷新动态库:sudo ldconfig
protobuf配置文件的编写
示例:
配置文件的后缀名为 .proto,按照如下格式定义消息类型。
syntax = "proto3"; // 声明了protobuf的版本
package fixbug; // 声明了代码所在的包(对于C++来说是namespace)
// 登录消息类型
message LoginRequest
{
string name = 1; // 1表示第一个字段
string pwd = 2;
}
// 定义登录响应消息类型
message LoginResponse
{
int32 errcode = 1;
string errmsg = 2;
bool success = 3;
}
编译命令:
首先使用命令 protoc 可以查看提示命令

这里可以看到,想要生成哪个语言的头文件和源文件,有对应的命令
使用命令:./表示在当前目录下生成
protoc test.proto --cpp_out=./


使用示例
序列化
使用刚才编译生成的test.pb.h里的类型
#include <iostream>
#include <string>
#include "test.pb.h"
using namespace fixbug;
int main(void)
{
// 封装了Login请求对象的信息
LoginRequest req;
req.set_name("zhang san");
req.set_pwd("123456");
// 对象数据序列化
std::string send_str;
if (req.SerializeToString(&send_str))
{
std::cout << send_str.c_str() << std::endl;
}
return 0;
}


反序列化
#include <iostream>
#include <string>
#include "test.pb.h"
using namespace fixbug;
int main(void)
{
// 封装了Login请求对象的信息
LoginRequest req;
req.set_name("zhang san");
req.set_pwd("123456");
// 对象数据序列化
std::string send_str;
if (req.SerializeToString(&send_str))
{
std::cout << send_str.c_str() << std::endl;
}
// 从send_str里反序列化一个login对象
LoginRequest req2;
if (req2.ParseFromString(send_str))
{
std::cout << req2.name() << std::endl;
std::cout << req2.pwd() << std::endl;
}
return 0;
}

使用经验
一般而言,在protobuf的配置文件里会把字符串类型设置为bytes类型
// 登录消息类型
message LoginRequest
{
bytes name = 1; // 1表示第一个字段
bytes pwd = 2;
}
在编写过程中如果有重复代码,可以把重复的拿出来写成一个message。
示例,在LoginResponse 和GetFriendListsResponse都有 errcode字段和errmsg字段,可以把这两个重复的字段写成一个message,在其他两个地方使用该类型即可。
message ResoultCode
{
int32 errcode = 1;
bytes errmsg = 2;
}
// 登录消息类型
message LoginRequest
{
bytes name = 1; // 1表示第一个字段
bytes pwd = 2;
}
// 定义登录响应消息类型
message LoginResponse
{
ResoultCode resoult = 1;
bool success = 3;
}
message GetFriendListsRequest
{
uint32 userid = 1;
}
message GetFriendListsResponse
{
ResoultCode resoult = 1;
}
protobuf列表的创建
在上面的示例中,所编写的是普通的数据类型,protobuf除了数据对象,还有列表和映射表。
如下,想要得到一个用户的好友信息,定义完好友信息后,再GetFriendListsResponse里要定义好友列表字段,就需要用到关键字 repeated。
syntax = "proto3"; // 声明了protobuf的版本
package fixbug; // 声明了代码所在的包(对于C++来说是namespace)
message ResoultCode
{
int32 errcode = 1;
bytes errmsg = 2;
}
// 登录消息类型
message LoginRequest
{
bytes name = 1; // 1表示第一个字段
bytes pwd = 2;
}
// 定义登录响应消息类型
message LoginResponse
{
ResoultCode resoult = 1;
bool success = 3;
}
message GetFriendListsRequest
{
uint32 userid = 1;
}
message User
{
bytes name = 1;
uint32 age = 2;
enum Sex
{
MAN = 0;
WOMAN = 1;
}
Sex sex = 3;
}
message GetFriendListsResponse
{
ResoultCode resoult = 1;
repeated User friend_list = 2; // 列表类型
}
使用示例:
#include <iostream>
#include <string>
#include "test.pb.h"
using namespace fixbug;
int main(void)
{
GetFriendListsResponse rsp;
ResultCode* rc = rsp.mutable_result();
rc->set_errcode(0);
User* user1 = rsp.add_friend_list();
user1->set_name("zhang san");
user1->set_age(20);
user1->set_sex(User::MAN);
User* user2 = rsp.add_friend_list();
user2->set_name("li si");
user2->set_age(21);
user2->set_sex(User::MAN);
std::cout << rsp.friend_list_size() << std::endl;
return 0;
}

如果要输出好友的信息,也很简单,friend_list()返回的是一个User类型,所以用该类型接收即可。
代码:
#include <iostream>
#include <string>
#include "test.pb.h"
using namespace fixbug;
int main(void)
{
GetFriendListsResponse rsp;
ResultCode* rc = rsp.mutable_result();
rc->set_errcode(0);
User* user1 = rsp.add_friend_list();
user1->set_name("zhang san");
user1->set_age(20);
user1->set_sex(User::MAN);
User* user2 = rsp.add_friend_list();
user2->set_name("li si");
user2->set_age(21);
user2->set_sex(User::MAN);
std::cout << rsp.friend_list_size() << std::endl;
User friend1 = rsp.friend_list(0);
User friend2 = rsp.friend_list(1);
std::cout << friend1.name() << std::endl;
std::cout << friend1.age() << std::endl;
std::cout << friend1.sex() << std::endl;
std::cout << friend2.name() << std::endl;
std::cout << friend2.age() << std::endl;
std::cout << friend2.sex() << std::endl;
return 0;
}

如果一个数据对象的某个成员还是对象的话,需要用指针接收一个mutable的地址。

定义描述RPC方法的类型-service
在protobuf中,默认不生成service服务类型,需要加上一个option选项。
// 定义该选项,表示生成service服务类和RPC方法描述,默认不生成
option cc_generic_services = true;
根据上面所编写的配置文件,可以得知service服务类可以这样写:
// 在protobuf里怎么定义描述rpc方法的类型 - service
service UserServiceRpc
{
rpc Login(LoginRequest) returns(LoginResponse);
rpc GetFriendLists(GetFriendListsRequest) returns(GetFriendListsResponse);
}
生成代码规则
那么它生成的C++代码是什么样子的?
以LoginRequest为例:它是一个class类,继承于 google::protobuf::Message


同样,request也是class类,继承Message
图示:

那么编写的service类型所生成的代码如下:
可以发现,生成的类和所编写的rpc方法一致。而生成的这个类就是
Callee ServiceProvider rpc服务提供者
class UserServiceRpc_Stub;
class UserServiceRpc : public ::PROTOBUF_NAMESPACE_ID::Service {
protected:
// This class should be treated as an abstract interface.
inline UserServiceRpc() {
};
public:
virtual ~UserServiceRpc();
typedef UserServiceRpc_Stub Stub;
static const ::PROTOBUF_NAMESPACE_ID::ServiceDescriptor* descriptor();
virtual void Login(::PROTOBUF_NAMESPACE_ID::RpcController* controller,
const ::fixbug::LoginRequest* request,
::fixbug::LoginResponse* response,
::google::protobuf::Closure* done);
virtual void GetFriendLists(::PROTOBUF_NAMESPACE_ID::RpcController* controller,
const ::fixbug::GetFriendListsRequest* request,
::fixbug::GetFriendListsResponse* response,
::google::protobuf::Closure* done);
// implements Service ----------------------------------------------
const ::PROTOBUF_NAMESPACE_ID::ServiceDescriptor* GetDescriptor();
/* 其他内容 */
};
第二个生成的是stub
caller ServiceConsumer rpc服务消费者
class UserServiceRpc_Stub : public UserServiceRpc {
public:
UserServiceRpc_Stub(::PROTOBUF_NAMESPACE_ID::RpcChannel* channel);
UserServiceRpc_Stub(::PROTOBUF_NAMESPACE_ID::RpcChannel* channel,
::PROTOBUF_NAMESPACE_ID::Service::ChannelOwnership ownership);
~UserServiceRpc_Stub();
inline ::PROTOBUF_NAMESPACE_ID::RpcChannel* channel() {
return channel_; }
// implements UserServiceRpc ------------------------------------------
void Login(::PROTOBUF_NAMESPACE_ID::RpcController* controller,
const ::fixbug::LoginRequest* request,
::fixbug::LoginResponse* response,
::google::protobuf::Closure* done);
void GetFriendLists(::PROTOBUF_NAMESPACE_ID::RpcController* controller,
const ::fixbug::GetFriendListsRequest* request,
::fixbug::GetFriendListsResponse* response,
::google::protobuf::Closure* done);
private:
::PROTOBUF_NAMESPACE_ID::RpcChannel* channel_;
bool owns_channel_;
GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(UserServiceRpc_Stub);
};
注意该类的构造函数需要传入一个Channel类型,该类型是一个抽象基类:
class PROTOBUF_EXPORT RpcChannel {
public:
inline RpcChannel() {
}
virtual ~RpcChannel();
// Call the given method of the remote service. The signature of this
// procedure looks the same as Service::CallMethod(), but the requirements
// are less strict in one important way: the request and response objects
// need not be of any specific class as long as their descriptors are
// method->input_type() and method->output_type().
virtual void CallMethod(const MethodDescriptor* method,
RpcController* controller, const Message* request,
Message* response, Closure* done) = 0;
private:
GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(RpcChannel);
};
那么对于上面的示例,在调用Login()方法或GetFriendLists()方法时,会在底层转为调用
channel_->CallMethod()
该方法是个纯虚函数,需要派生类继承RpcChannel类后重写该方法(因为基类指针可以指向派生类对象,所以调用的方法会是派生类实现的CallMethod()方法)。
图示:

边栏推荐
- [information system project manager] summary of essence of high-level series for the first time
- JS judge the data types object.prototype.tostring.call and typeof
- A new paradigm of distributed deep learning programming: Global tensor
- 解决ProxyError: Conda cannot proceed due to an error in your proxy configuration.
- E. Two Small Strings
- asp. Net using redis cache
- Sublime install plug-ins
- Xiaobai makes a wave of deep copy and shallow copy
- SSG framework Gatsby accesses the database and displays it on the page
- Learning notes: what are the common array APIs that change the original array or do not change the original array?
猜你喜欢

The diagram of user login verification process is well written!

服务器、客户端双认证(2)

QT handy notes (III) use qtcharts to draw a line chart in VS

Spolicy request case

Fuzzy PID control of motor speed

The problem of accessing certsrv after configuring ADCs

SSG framework Gatsby accesses the database and displays it on the page

Solve NPM -v sudden failure and no response

Uni app learning summary

I finished watching this video on my knees at station B
随机推荐
IIS网站配置
copyTo
E. Two Small Strings
解决npm -v突然失效 无反应
Search module use case writing
(1) Hand eye calibration of face scanner and manipulator (eye on hand)
Nodejs service background execution (forever)
服务发现原理分析与源码解读
Encapsulation of tabbarcontroller
小白搞一波深拷贝 浅拷贝
asp. Net using redis cache (2)
Node 内存溢出及V8垃圾回收机制
Learning notes: what are the common array APIs that change the original array or do not change the original array?
[information system project manager] summary of essence of high-level series for the first time
M-ary number STR to n-ary number
官方颁发的SSL证书与自签名证书结合实现网站双向认证
Solve the problem of storing cookies in IE7 & IE8
Application of Gauss elimination
How to add a PDB
高斯消元求解异或线性方程组