当前位置: 首页 > article >正文

protobuf学习使用

1、概述

protobuf是Google开发的一种语言中立、平台无关、可扩展的序列化结构数据格式。允许定义一次数据结构,然后可以使用各种支持的语言来生成代码,以轻松地读写这些结构到一个二进制流中,如网络传输或文件,Protobuf支持多种编程语言,包括但不限于C++、Java、Python、Go、Ruby、JavaScript、C#等。

protobuf-getting-started:Protocol Buffer Basics: C++ | Protocol Buffers Documentation

2、使用步骤(以C++举例)

1、创建并编写xxx.proto文件

        将要序列化的数据写到proto文件

2、将proto文件生成对应的.cc和.h文件

        protoc -I=/路径1 --cpp_out=./路径2 /路径1/addressbook.proto

        路径1为.proto所在的路径

        路径2为.cc和.h⽣成的位置
protoc.exe -I=./ --cpp_out=./ *.proto

 3、直接使用新生成的类

        里边有对数据操作的api

3、使用说明

3.1、编写xxx.proto文件  

举例login.proto

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

// 定义请求消息
message LoginRequest {
  // 语法格式:字段类型 字段名 = 字段编号;
  // 编号从1开始,不能重复
  bytes username = 1;
  string password = 2;
  int32 type = 3;
}

// 定义响应消息
message LoginResponse {
  string token = 1;
  bytes message = 2;
}

message相当于C++的class关键字,LoginRequest相当于类名,注释采用C/C++风格的双斜杠(//)

username、password、type相当于成员变量。编号从1开始,不能重复,序列化后的protobuf没有使用字段名称,而仅仅采用了字段编号区分字段,与json xml等相比,protobuf不是一种完全自描述的协议格式,即接收端在没有proto文件定义的前提下是无法解码一个protobuf消息体的,与此相对的,json xml等协议格式是完全自描述的,拿到了json消息体,便可以知道这段消息体中有哪些字段。

语法格式
字段类型 字段名 = 字段编号

如何知道有哪些字段类型呢,可以对照下面表查看

3.2、将proto文件生成对应的.cc和.h文件

protoc -I=./ --cpp_out=./ *.proto

其中login.pb.h和login.pb.cc为新生成的头文件和源文件。
下载链接比如:https://github.com/protocolbuffers/protobuf/releases/tag/v26.1

此时我们可以打开看看新生成的头文件,会发现其中有相关字段读写api,详见下图:

3.3、使用代码范例

范例代码演示了如何使用新生成的类,如何序列化和反序列化

代码编译:g++ -std=c++11 -o main login.pb.cc main.cc -lprotobuf -lpthread -L/usr/local/lib

#include "login.pb.h"
#include <iostream>
using namespace std;

int main(int argc, char *argv[]) {

    // 创建一个 LoginRequest 对象
    LoginRequest login_request;
    login_request.set_username("张三");
    login_request.set_password("123");
    login_request.set_type(100);

    // 将 LoginRequest 对象序列化为字符串
    string request_data;
    login_request.SerializeToString(&request_data);

    // 将字符串发送到服务器....

    // 服务器接收到字符串后,反序列化为 LoginRequest 对象
    LoginRequest login_response;
    login_response.ParseFromString(request_data);

    // 使用 LoginRequest 对象进行业务逻辑处理...
    cout << "接收 用户名: " << login_response.username() << endl;
    cout << "接收 密码: " << login_response.password() << endl;
    cout << "接收 类型: " << login_response.type() << endl;

    return 0;
}

4、protobuf中使用数组

使用数组要使用repeated修饰相关字段,此时你可以理解为vector<string> name,如图。

#include "login.pb.h"
#include <iostream>
using namespace std;

int main(int argc, char *argv[]) {

    // 创建一个 LoginRequest 对象
    LoginRequest login_request;
    // login_request.set_username("张三");

    // username 是一个 repeated 字段,可以添加多个用户名
    login_request.add_username("张三");
    login_request.add_username("李四");
    login_request.add_username("王五");

    login_request.set_password("123");
    login_request.set_type(100);

    // 将 LoginRequest 对象序列化为字符串
    string request_data;
    login_request.SerializeToString(&request_data);

    // 将字符串发送到服务器....

    // 服务器接收到字符串后,反序列化为 LoginRequest 对象
    LoginRequest login_response;
    login_response.ParseFromString(request_data);

    // 使用 LoginRequest 对象进行业务逻辑处理...
    // cout << "接收 用户名: " << login_response.username() << endl;
    cout << "接收 用户名: " << login_response.username(0) << endl;
    cout << "接收 用户名: " << login_response.username(1) << endl;
    cout << "接收 用户名: " << login_response.username(2) << endl;

    cout << "接收 密码: " << login_response.password() << endl;
    cout << "接收 类型: " << login_response.type() << endl;

    return 0;
}

5、protobuf中使用枚举

在proto文件中添加枚举定义(如图),这里有一个注意事项,枚举的第一个元素值必须为0,为了兼容proto2语义。

#include "login.pb.h"
#include <iostream>
using namespace std;

int main(int argc, char *argv[]) {

    // 创建一个 LoginRequest 对象
    LoginRequest login_request;
    // login_request.set_username("张三");

    // username 是一个 repeated 字段,可以添加多个用户名
    login_request.add_username("张三");
    login_request.add_username("李四");
    login_request.add_username("王五");

    login_request.set_password("123");
    login_request.set_type(100);

    // 设置枚举值
    login_request.set_logintype(ADMIN);

    // 将 LoginRequest 对象序列化为字符串
    string request_data;
    login_request.SerializeToString(&request_data);

    // 将字符串发送到服务器....

    // 服务器接收到字符串后,反序列化为 LoginRequest 对象
    LoginRequest login_response;
    login_response.ParseFromString(request_data);

    // 使用 LoginRequest 对象进行业务逻辑处理...
    // cout << "接收 用户名: " << login_response.username() << endl;
    cout << "接收 用户名: " << login_response.username(0) << endl;
    cout << "接收 用户名: " << login_response.username(1) << endl;
    cout << "接收 用户名: " << login_response.username(2) << endl;

    cout << "接收 密码: " << login_response.password() << endl;
    cout << "接收 类型: " << login_response.type() << endl;

    // 打印 枚举登陆类型
    cout << "接收 类型: " << login_response.logintype() << endl;

    return 0;
}

6、protobuf中导入其他proto文件

新编写extrainfo.proto,使用import导入,然后使用protoc工具生成新头文件和源文件。
范例代码演示了如何操作Extrainfo中的成员。
备注:相关接口在生成的头文件中都能看到,可以打开看看

代码编译:g++ -std=c++11 -o main login.pb.cc main.cc extrainfo.pb.cc -lprotobuf -lpthread -L/usr/local/lib

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

message Extrainfo {
  string time = 1;
  int32 level = 2;
}
// 声明protobuf版本
syntax = "proto3";
// 导入其他proto文件
import "extrainfo.proto";

enum LoginType {
  USER = 0; // proto3中第一个枚举值必须为0
  ADMIN = 1;
}

// 定义请求消息
message LoginRequest {
  // 语法格式:字段类型 字段名 = 字段编号;
  // 编号从1开始,不能重复
  repeated bytes username = 1;
  string password = 2;
  int32 type = 3;
  LoginType loginType = 4;
  Extrainfo extra = 5;
}

// 定义响应消息
message LoginResponse {
  string token = 1;
  bytes message = 2;
}
#include "login.pb.h"
#include <iostream>
using namespace std;

int main(int argc, char *argv[]) {

    // 创建一个 LoginRequest 对象
    IMLogin::LoginRequest login_request;
    // login_request.set_username("张三");

    // username 是一个 repeated 字段,可以添加多个用户名
    login_request.add_username("张三");
    login_request.add_username("李四");
    login_request.add_username("王五");

    login_request.set_password("123");
    login_request.set_type(100);

    // 设置枚举值
    login_request.set_logintype(ADMIN);

    // 操作其他proto,extra字段
    login_request.mutable_extra()->set_time("key");
    login_request.mutable_extra()->set_level(6);

    // 将 LoginRequest 对象序列化为字符串
    string request_data;
    login_request.SerializeToString(&request_data);

    // 将字符串发送到服务器....

    // 服务器接收到字符串后,反序列化为 LoginRequest 对象
    LoginRequest login_response;
    Extrainfo extra = login_response.extra();
    login_response.ParseFromString(request_data);

    // 使用 LoginRequest 对象进行业务逻辑处理...
    // cout << "接收 用户名: " << login_response.username() << endl;
    cout << "接收 用户名: " << login_response.username(0) << endl;
    cout << "接收 用户名: " << login_response.username(1) << endl;
    cout << "接收 用户名: " << login_response.username(2) << endl;

    cout << "接收 密码: " << login_response.password() << endl;
    cout << "接收 类型: " << login_response.type() << endl;

    // 打印 枚举登陆类型
    cout << "接收 类型: " << login_response.logintype() << endl;

    // 访问其他proto,extra字段
    cout << "接收 extra 字段: " << extra.time() << endl;
    cout << "接收 extra 字段: " << extra.level() << endl;

    return 0;
}

7、protobuf中添加命名空间

在proto文件中添加package IMLogin,表示当前的proto文件命名空间为IMLogin,打开生成的头文件一看就懂,C++代码再定义对象时也要添加上命名空间。

8、protobuf编译安装

9、问题:使用protobuf为什么节省字节

1、变量名不用传递,传编号(按编号区分字段)

2、采用可变长字段方案(可自行了解下)

学习链接:https://github.com/0voice


http://www.kler.cn/a/453949.html

相关文章:

  • 零基础微信小程序开发——页面导航之编程式导航(保姆级教程+超详细)
  • 如何为运行在 PICO 4 Ultra 设备上的项目设置外部文件读写权限?
  • LangChain教程 - 表达式语言 (LCEL) -构建智能链
  • Docker部署GitLab服务器
  • [论文笔记] 从生成到评估:LLM-as-a-judge 的机遇与挑战
  • 记录一次前端绘画海报的过程及遇到的几个问题
  • 面试题整理12----K8s中Pod创建常见错误
  • android webview 从配置文件加载网页——未来之窗跨平台架构
  • Linux文件目录 --- mkdir命令,创建目录,多级目录,设置目录权限
  • 《探索 Apache Spark MLlib 与 Java 结合的卓越之道》
  • 图【东北大学oj数据结构11-1】C++
  • kafka的备份策略:从备份到恢复
  • HDR视频技术之十一:HEVCH.265 的 HDR 编码方案
  • IP-trunk,HDLC链路的捆绑
  • XMLHttpRequest的基础知识
  • java中两个系统进行非对称加密,两个系统的公私钥可以用一套吗?
  • CSS系列(38)-- Custom Properties高级应用详解
  • 基于 Python 大数据的计算机就业数据分析系统
  • 网络基础知识--4
  • 实战举例——vue.js组件开发
  • VMD-SSA-BiLSTM、VMD-BiLSTM、BiLSTM时间序列预测对比
  • sql-DQL(持续更新中...)
  • OCR(二) TesseractOCR 语言包训练
  • Linux内核 -- UIO (User-space I/O) 简介与使用笔记
  • 使用Grafana中按钮插件实现收发HTTP请求
  • 【C语言】矩阵乘法