proto库的基本使用

proto库

protobuf 它能够将 一个对象 编码成二进制,作为传输的中间文件 ,并能够将中间文件解析回原数据创建.proto文件 ,定义好相应的数据类型

编写 xxx.proto文件

protoc test.proto --cpp_out=“./” // 生成相应的代码,包含数据类型的编码

// main.cpp 为调用proto的代码, 链接动态库进行编译

g++ test.pb.cc main.cpp -o test pkg-config --cflags --libs protobuf -lpthread

文件头

test.proto

// 指明版本及包名
syntax="proto3";// 声明proto版本

package fixbug;// 声明代码所在的包(对c++来说就是namespace)

基本对象的定义

test.proto

string 定义成byte会更好


// 定义登陆请求消息类型
message LoginRequest
{
    string name=1; // 表明第一个字段
    string pwd=2; // 定义第二个字段
}
// 定义登陆响应消息类型
message LoginResponse
{
    int32 errcode=1;
    string errmsg=2;
    bool success=3;
}
基本对象的初始化与赋值

main.cpp

  LoginRequest req;
    req.set_name("zhang san");
    req.set_pwd("123456");
基本对象的编码与解码
  
// 编码
string send_str;
    if(req.SerializeToString(&send_str))
    {
        cout<<send_str.c_str()<<endl;
    }
// 解码
LoginRequest reqB;
    if(reqB.ParseFromString(send_str))
    {
        cout<<reqB.name()<<endl;
        cout<<reqB.pwd()<<endl;
    }
对象中的“组合" 定义

一个对象中包含另一个对象即为组合

test.proto

message ResultCode
{
    int32 errcode=1;
    string errmsg=2;
}

// 定义登陆响应消息类型
message LoginResponse
{
    ResultCode result=1;
    bool success=2;
}
对象中的"组合" 初始化与赋值

main.cpp

LoginResponse rsp;
    ResultCode *rc=rsp.mutable_result();
    rc->set_errcode(1);
    rc->set_errmsg("登陆处理失败");
列表定义
message User
{
    bytes name;
    uint32 age=2;
    enum Sex
    {
        MAN=0;
        WOMAN=1;
    }
    Sex sex=3;
}
message GetFriendListsResponse
{
    ResultCode result=1;
    repeated User users=2;
}
列表的初始化

test.proto

GetFriendListsResponse rlist;
    ResultCode* rc2=rlist.mutable_result();
    rc2->set_errcode(0);

    User *user1=rlist.add_friend_lists();
    user1->set_name("zhang san");
    user1->set_age(20);
    user1->set_sex(User::MAN);

    User *user2=rlist.add_friend_lists();
    user2->set_name("zhang san");
    user2->set_age(20);
    user2->set_sex(User::MAN);
遍历列表

main.cpp

cout<<rlist.friend_lists_size()<<endl;
    for(auto item:rlist.friend_lists())
    {
        cout<<item.name()<<endl;
        cout<<item.age()<<endl;
    }

     return 0;

创建 XXX.proto 文件// 指明版本及包名

rpc service

在前面protobuf中 定义了对象

接下来要在protobuf 中,定义相关的函数

函数的注册

.proto

// 定义下列选项, 表明rpc服务定义方法需要生成
option cc_generic_services=true;

// 在proto中定义rpc方法类型  必须定义option 才能生成该代码
service UserServiceRpc 
{
    rpc Login(LoginRequest) returns(LoginResponse);
    rpc GetFriendLists(GetFriendListsRequest) 	returns(GetFriendListsResponse); 
}
底层
message
定义一个数据对象
继承 google::protobuf::Service
提供
	setXXX() //数据初始化方法
	XXX() // 数据访问方法
	编码方法
	解码方法
service
callee ServiceProvider rpc 提供者 继承至google service

UserServiceRpc

具有默认构造函数,不需要传入任何参数

包含两个函数 及相应的参数 输入的参数作为一个const 输出的参数 作为一个空变量提供进去

class UserServiceRpc : public ::PROTOBUF_NAMESPACE_ID::Service {
 

  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);
caller ServiceConsumer rpc 服务消费者。继承至 UserServiceRpc( 提供者类)
UserServiceRpc_Stub 代理类

本地调用-> stub -> 网络 -> stub -> 服务器执行函数

无默认构造函数,需要传入channel

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);
};

去看该方法实现的时候 会发现

void UserServiceRpc_Stub::Login(::PROTOBUF_NAMESPACE_ID::RpcController* controller,
                              const ::fixbug::LoginRequest* request,
                              ::fixbug::LoginResponse* response,
                              ::google::protobuf::Closure* done) {
  channel_->CallMethod(descriptor()->method(0),
                       controller, request, response, done);
}
void UserServiceRpc_Stub::GetFriendLists(::PROTOBUF_NAMESPACE_ID::RpcController* controller,
                              const ::fixbug::GetFriendListsRequest* request,
                              ::fixbug::GetFriendListsResponse* response,
                              ::google::protobuf::Closure* done) {
  channel_->CallMethod(descriptor()->method(1),
                       controller, request, response, done);
}

调用函数的时候 , 会自动 调用 channel_->CalllMethod()

我们去看下 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);
};

会发现 channel 是一个抽象类 我们必须 继承实现该类

在方法中实现对象的序列化 及网络传输等等

method 为方法

controller

request 参数对象

response 响应对象

class mychannel:public RpcChannel
{
void callmeMethod(const MethodDescriptor* method,
RpcController* controller, const Message* request,
Message* response, Closure* done)
{

}

}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
在 Go 中使用 Protocol Buffers(简称 proto)可以通过以下步骤进行: 1. 安装 protoc 编译器:Protocol Buffers 使用 protoc 编译器将 .proto 文件编译成 Go 代码。你可以从 [Protocol Buffers GitHub 仓](https://github.com/protocolbuffers/protobuf/releases) 下载适合你操作系统的 protoc 编译器,并将其添加到系统 PATH 环境变量中。 2. 编写 .proto 文件:创建一个名为 example.proto 的文本文件,并使用 Protocol Buffers 的语法定义消息结构和服务。 示例 example.proto 文件: ```protobuf syntax = "proto3"; package example; message Person { string name = 1; int32 age = 2; repeated string hobbies = 3; } service Greeting { rpc SayHello (Person) returns (Person); } ``` 3. 生成 Go 代码:使用 protoc 编译器将 .proto 文件生成 Go 代码。在终端中运行以下命令: ```shell protoc --go_out=. example.proto ``` 这将在当前目录下生成一个 example.pb.go 文件,其中包含了根据 .proto 文件生成的 Go 结构体和服务代码。 4. 在 Go 代码中使用生成的代码:在你的 Go 代码中导入生成的代码文件,并使用其中定义的结构体和服务进行编程。 示例 Go 代码: ```go package main import ( "fmt" "log" example "path/to/generated/code" // 导入生成的代码包 "google.golang.org/protobuf/proto" ) func main() { person := &example.Person{ Name: "John", Age: 25, Hobbies: []string{"reading", "coding"}, } data, err := proto.Marshal(person) if err != nil { log.Fatal(err) } // 在这里可以将 data 发送给其他地方进行传输或存储 newPerson := &example.Person{} err = proto.Unmarshal(data, newPerson) if err != nil { log.Fatal(err) } fmt.Println(newPerson.GetName()) // 输出:John } ``` 以上就是使用 Protocol Buffers 在 Go 中的基本步骤。你可以根据自己的需求定义更复杂的消息结构和服务,并使用生成的 Go 代码进行序列化、反序列化等操作。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值