自学内容网 自学内容网

rpc中常用的数据格式:Protobuf 图文详解

概述

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初看有些复杂,但其实都是有规律的

image-20241012201312528

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 文件中定义的 LoginGetFriendList 函数,本质上是调用 CallMethod 函数(channel_->CallMethod(descriptor()->method(0),controller, request, response, done);
  • 再追溯一下,RpcChannel 是一个类,里面是 CallMethod 函数是虚函数,这说明 RpcChannel 类以及它的 CallMethod 函数,都需要我们开发者进行重写

序列化与反序列化

  • 序列化是将数据结构或对象转换为可以存储或传输的格式(通常是字节序列/string类型)的过程。

    其目的是将复杂的数据结构转化为一种便于存储、传输或在不同系统之间交换的形式。

  • 反序列化则是序列化的逆过程,它将序列化后的数据(如字节流)重新转换回原始的数据结构或对象。

    假设我们有一个包含学生姓名、年龄和成绩的对象。序列化可能会将这个对象转换为一个特定格式的字符串,如 {“name”:“张三”,“age”:20,“score”:90} 。而反序列化就是把接收到的这样的字符串重新转换回包含姓名、年龄和成绩的学生对象。

image-20241012204439211

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

原文地址:https://blog.csdn.net/henghuizan2771/article/details/142886403

免责声明:本站文章内容转载自网络资源,如本站内容侵犯了原著者的合法权益,可联系本站删除。更多内容请关注自学内容网(zxcms.com)!