protobuf入门实践1

protobuf入门实践1

下载和安装

protobuf:https://github.com/google/protobuf
解压压缩包: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

完成之后输入protoc,如下输出即安装成功
protobuf入门实践1_第1张图片

proto配置文件

syntax = "proto3"; //声明protobuf的版本

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

//定义登录请求消息类型   name   pwd
message LoginRequest{
    bytes name = 1;   //等于1表示这是第一个参数,一般string的存储定义为bytes
    bytes pwd = 2;
}

//定义登录响应消息类型
message LoginResponse{
    int32 errcode = 1;
    bytes errmsg = 2;
    bool success = 3;
}

xxx.proto文件定义了protobuf的版本,更重要的是定义了用户后面需要序列和反序列化的自定义消息类型,这会当做后面的远程rpc调用的参数类型。
message是protobuf内置的抽象类message,用于定义远程rpc传输的各种消息类型,语法是:
message{数据类型 变量名 = index}, 数据类型既可以是内置的基本的数据类型也可以是其他message类型。
上述简单定义了一个登陆所需要的请求消息以及响应消息。

protobuf支持多种语言,可以将上述proto配置文件编译成所支持的任意语言,例如c++、java等等
下面通过protoc命令编译为c++版本
输入protoc xxx.proto --cpp_out=./, 表示在当前目录下生产proto配置文件所对应用户端可以使用的文件(C++源文件)如下:
在这里插入图片描述
如何使用这些源文件?

#include "test.pb.h"
#include 
#include 

using namespace fixbug;

int main(){    
    //封装login请求对象的数据
    LoginRequest req;
    req.set_name("zhang san");
    req.set_pwd("123456");

    //将LoginRequest对象序列化成字节数组(char*)
    std::string send_str;
    if(req.SerializeToString(&send_str)){
         std::cout<< send_str.c_str() << "\n";
    }

    //从send_str反序列化出一个login请求对象
    LoginRequest reqB;
    if(reqB.ParseFromString(send_str)){
        std::cout<<"name:"<<reqB.name()<<"\n"<<"pwd:"<<reqB.pwd()<<"\n";
    }

    return 0;
}

执行g++ main.cc test.pb.cc -lprotobuf && ./a.out
在这里插入图片描述
应该看起来还挺简单的,需要注意的是ResultCode变量的获取是,fixbug就是namespace fixbug, 然后每个消息类型对应在xxx.pb.cc文件中就是再fixbug命名空间下的一个类,消息类型里面定义的参数类型就是类里面的成员变量,并提供了这些成员变量的set_xxx(),xxx()方法来用于设置这些成员变量和获取该变量。是不是这样呢?看看生成的xxx.pb.h类吧:

namespace fixbug {
class GetFriendsListRequest;
class GetFriendsListRequestDefaultTypeInternal;
extern GetFriendsListRequestDefaultTypeInternal _GetFriendsListRequest_default_instance_;
class GetFriendsListResponse;
class GetFriendsListResponseDefaultTypeInternal;
extern GetFriendsListResponseDefaultTypeInternal _GetFriendsListResponse_default_instance_;
class LoginRequest;
class LoginRequestDefaultTypeInternal;
extern LoginRequestDefaultTypeInternal _LoginRequest_default_instance_;
class LoginResponse;
class LoginResponseDefaultTypeInternal;
extern LoginResponseDefaultTypeInternal _LoginResponse_default_instance_;
class ResultCode;
class ResultCodeDefaultTypeInternal;
extern ResultCodeDefaultTypeInternal _ResultCode_default_instance_;
class User;
class UserDefaultTypeInternal;
extern UserDefaultTypeInternal _User_default_instance_;
}  // namespace fixbug

可以很清楚的看到,的确是这样,定义的消息数据类型都是一个个类,那么看看具体的一个LoginRequest类:

class LoginRequest :
    public ::PROTOBUF_NAMESPACE_ID::Message /* @@protoc_insertion_point(class_definition:fixbug.LoginRequest) */ {
 public:
  LoginRequest();
  virtual ~LoginRequest();

  LoginRequest(const LoginRequest& from);
  LoginRequest(LoginRequest&& from) noexcept
    : LoginRequest() {
    *this = ::std::move(from);
  }
	.....
   ......
  // bytes name = 1;
  void clear_name();
  const std::string& name() const;
  void set_name(const std::string& value);
  void set_name(std::string&& value);
  void set_name(const char* value);
  void set_name(const void* value, size_t size);
  std::string* mutable_name();
  std::string* release_name();
  void set_allocated_name(std::string* name);
  private:
  const std::string& _internal_name() const;
  void _internal_set_name(const std::string& value);
  std::string* _internal_mutable_name();
  public:

  // bytes pwd = 2;
  void clear_pwd();
  const std::string& pwd() const;
  void set_pwd(const std::string& value);
  void set_pwd(std::string&& value);
  void set_pwd(const char* value);
  void set_pwd(const void* value, size_t size);
  std::string* mutable_pwd();
  std::string* release_pwd();
  void set_allocated_pwd(std::string* pwd);
  private:
  const std::string& _internal_pwd() const;
  void _internal_set_pwd(const std::string& value);
  std::string* _internal_mutable_pwd();
  
  private:
  class _Internal;

  ::PROTOBUF_NAMESPACE_ID::internal::InternalMetadataWithArena _internal_metadata_;
  ::PROTOBUF_NAMESPACE_ID::internal::ArenaStringPtr name_; 
  ::PROTOBUF_NAMESPACE_ID::internal::ArenaStringPtr pwd_;
  mutable ::PROTOBUF_NAMESPACE_ID::internal::CachedSize _cached_size_;
  friend struct ::TableStruct_test_2eproto;
.....
};

可以看到的确如我们所说。

再来修改一下proto文件,介绍一下列表的使用,因为在参数调用过程中,要么就是单个数据,要么就是该数据组成的列表,当然还有映射类型(有兴趣可以自行了解)

syntax = "proto3"; //声明protobuf的版本

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

message ResultCode{  //定义返回的错误码
    int32 errcode = 1;
    bytes errmsg = 2;
}

//定义登录请求消息类型   name   pwd
message LoginRequest{
    bytes name = 1;   //等于1表示这是第一个参数,一般string的存储定义为bytes
    bytes pwd = 2;
}

//定义登录响应消息类型
message LoginResponse{
    ResultCode result = 1;
    bool success = 3;
}
message GetFriendsListRequest{
    uint32 user_id = 1;
}

message User{
    bytes name = 1;
    uint32 age = 2;
    enum Sex{
        MAN = 0;
        WOMAN = 1;
    }
    Sex sex = 3;
}

message GetFriendsListResponse{
    ResultCode result = 1;
    repeated User friend_list = 2;  //定义了一个列表数据类型
}

这里新增了三个消息类型,分别是User、GetFriendsListRequest、GetFriendsListResponse, 在GetFriendsListResponse消息类型中repeated 关键字是定义多个User,即一个User列表,这里需要注意的是为了避免代码重复,将错误码errcode和错误消息errmsg抽象成一个单独的消息类型。
老样子:protoc xxx.proto --cpp_out=./

#include "test.pb.h"
#include 
#include 

using namespace fixbug;


int main(){
    
    // LoginResponse rsp;
    // ResultCode* res = rsp.mutable_result();
    // rsp.set_success(0);
    // res->set_errcode(1);
    // res->set_errmsg("login failed");

    GetFriendsListResponse list; 

    //列表操作
    ResultCode* rc = list.mutable_result();
    rc->set_errcode(0);
    rc->set_errmsg("");

	//添加用户
    User* u1 = list.add_friend_list();
    u1->set_name("zs");
    u1->set_age(21);
    u1->set_sex(User::MAN);

    User* u2 = list.add_friend_list();
    u2->set_name("ls");
    u2->set_age(21);
    u2->set_sex(User::MAN);
    
    std::cout<<"list size = "<<list.friend_list_size()<<"\n";
    for(int i = 0; i<list.friend_list_size(); i++){
        User u = list.friend_list(i);
        std::cout<<"name : "<< u.name()<<" ";
        std::cout<<"age : "<<u.age()<<" ";
        std::cout<<"sex : "<<u.sex()<<"\n";
    }
    return 0;
}

错误码和错误消息由于也封装成了一个消息,这里获取是通过::mutable_result()方法返回一个该变量的指针,对该指针的修改就行对原ResultCode对象类型的赋值操作。后面就是列表的操作,通过add_friend_list()方法返回一个需要新添加的User,friend_list_size()用于获取列表的长度,friend_list()用于得到列表中某个索引的User对象。

执行结果:
list size = 2
name : zs age : 21 sex : 0
name : ls age : 21 sex : 0

你可能感兴趣的:(分布式,rpc)