protobuf基础学习

news2024/9/20 12:37:42

部分内容出自:https://blog.csdn.net/baidu_32237719/article/details/99723353
proto文件来预先定义的消息格式。数据包是按照proto文件所定义的消息格式完成二进制码流的编码和解码。proto文件,简单地说,就是一个消息的协议文件,这个协议文件的后缀文件名为“.proto”。proto其实就相当于接口的调用,类似于post,get等这些请求于响应处理,它也会携带信息输出,类似于请求头,请求头…

基础语法:

// 声明使用的语法是proto3,否则默认是proto2,一般写于第一行

syntax = "proto3";

拍//命名空间,包名,必须第二行

package google.protobuf;

//导包
如果想使用的消息类型定义在另一个.proto文件中,可以通过导包的方式将另一个文件导入进来,导包的语句如下:

import "google/protobuf/source_context.proto";

导入某个包,上面导入了source_context包,就可以支持Protobuf3的SourceContext类型。
例:test.proto中调用test2.proto文件中的DeviceInfo这个message,test2.proto的包名是 test2package
(1)import “test2.proto”; 添加你需要调用哪一个proto文件
(2)在test.proto中调用: test2package.DeviceInfo deviceInfo = 2;

syntax = "proto3";
package testpackge;
import "test2.proto"//导入test2.proto包
message Info{
    string id = 1;
    //调用Message
    test2package.DeviceInfo deviceInfo = 2;
}

//option可选项
//是否运行生成多个java文件

option java_multiple_files = false;

//这个选项表明生成java类所在的包。如果在.proto文件中没有明确的声明java_package,就采用默认的包名

option java_package = “com.example.administrator.grpctest.proto”;

//生成的java类名字

option java_outer_classname = “TestProto”;

//生成方式:可以被设置为 SPEED, CODE_SIZE,or LITE_RUNTIME。这些值将通过如下的方式影响C++及java代码的生成:

option optimize_for = SPEED;

① SPEED(默认值):
表示生成的代码运行效率高,但是由此生成的代码编译后会占用更多的空间。
② CODE_SIZE:
与SPEED恰恰相反,代码运行效率较低,但是由此生成的代码编译后会占用更少的空间,
通常用于资源有限的平台,如Mobile。
③ LITE_RUNTIME:
生成的代码执行效率高,同时生成代码编译后的所占用的空间也非常少。
这是以牺牲Protobuf提供的反射功能为代价的。
因此我们在C++中链接Protobuf库时仅需链接libprotobuf-lite,而非protobuf。
//1. SPEED模式:(自定义的类继承自 Message 类)
// .proto 文件:
option optimize_for = SPEED;
// .pb.h 文件:
class Person : public ::PROTOBUF_NAMESPACE_ID::Message {};
//2. CODE_SIZE模式:(自定义的类继承自 Message 类)
// .proto 文件:
option optimize_for = CODE_SIZE;
// .pb.h 文件:
class Person : public ::PROTOBUF_NAMESPACE_ID::Message {};
//3. LITE_RUNTIME模式:(自定义的类继承自 MessageLite 类)
// .proto 文件:
option optimize_for = LITE_RUNTIME;
// .pb.h 文件:
class Person : public ::PROTOBUF_NAMESPACE_ID::MessageLite {};

// 需要传输的数据格式
//定义消息:消息的结构体,以message标识。
格式顺序为:修饰符 参数类型 参数名字 = 标识符 [默认值]

message Api {
    string name = 1;
    repeated Method methods = 2;
    repeated Option options = 3;
    string version = 4;
    SourceContext source_context = 5;
    repeated Mixin mixins = 6 default = 123];        //数组
    Syntax syntax = 7;
}

//一个proto文件可以定义多个数据格式

enum FctaFrntLeWarnReq {
      FCTA_FRNT_LEFT_NO_WARNING       = 0;
      FCTA_FRNT_LEFT_WARNING_LEVEL_1  = 1;
      FCTA_FRNT_LEFT_WARNING_LEVEL_2  = 2;
      FCTA_FRNT_LEFT_WARNING_RESERVED = 3;
}

//也可以在数据格式里面嵌套另一个数据格式

message Info{
string name = 1;
 int age = 2;
Int sex = 3;
Job job = 4;
}
message Job{
string job = 1;
}

//字段规则 required : 字段只能也必须出现 1 次,多用于必填项,必须赋值的字符

required int32 id = 1 [default = 123]

//optional : optional #结合message使用,表示message字段的内容选填,可以传也可以不传,可以使用[default = xxx]配置默认值 例如:

optional string name = 1 [default = "张三"]

//repeated :#结合message使用,表示该字段接收或返回为数组,字段可出现任意多次(包括 0)
多用于 Java List 属性 例如:

//list String
repeated string strList = 5;
//list 对象
repeated Role roleList = 6;

//定义接口:接口路径和参数,以service标识。
#定义这个proto文件的方法集合,类似于方法接口
(1) 定义访问服务端的函数名称,传递参数,返回值
— 就是rpc通信中构建请求消息结构的 接口名称
(2) 一个service可以定义多个待调用的函数
(3) 定义方式:rpc 方法名 (参数) returns (返回值) 没有返回值 就写Empty
rpc #定义方法的关键字,结合service使用
returns #返回响应,结合service使用

service FctaAdBlindZoneFuncMgr {
    option(service_id) = 9008;
    rpc FctaOnOffSetMed(FctaOnOffSetMsg) returns (Null) {
        option (rpc_id) = 102;
        option (method_type) = METHOD;
    }
    rpc FctaWarnReqEvt(FctaWarnReqMsg) returns (Null) {
        option (rpc_id) = 100;
        option (method_type) = EVENT;
        option (update_policy) = ON_CHANGE;
    }
    rpc FctaOnOffStsFid(FctaOnOffStsMsg) returns (Null) {
        option (rpc_id) = 101;
        option (method_type) = FIELD_READONLY;
        option (update_policy) = ON_CHANGE;
    }
}

//保留标识符(reserved)

message Persion{
      string id = 1;
      string name = 2;
  
      //保留3,15,9,10,11这几个标识符不能被使用
      reserved  3, 15, 9 to 11;
      //reserved "location"
}

//emnu格式定义

  enum Corpus {
      UNIVERSAL = 0;
      WEB = 1;
      IMAGES = 2;
      LOCAL = 3;
      NEWS = 4;
      PRODUCTS = 5;
      VIDEO = 6;
  }

//可以将option属性中allow_alias 设置为true,来为枚举类型定义相同的常量值

  enum EnumAllowingAlias {
      option allow_alias = true;
      UNKNOWN = 0;
      STARTED = 1;
      RUNNING = 1;
  }

枚举常量值必须在32字节integer范围内。负数效率低,不推荐使用
//枚举嵌入在message中是不带标识符的

message SearchRequest {
    string query = 1;
    int32 page_number = 2;
    int32 result_per_page = 3;
    enum Corpus {
        UNIVERSAL = 0;
        WEB = 1;
        IMAGES = 2;
        LOCAL = 3;
        NEWS = 4;
        PRODUCTS = 5;
        VIDEO = 6;
    }
    Corpus corpus = 4;
}

枚举定义在一个消息内部或消息外部都是可以的,如果枚举是 定义在 message 内部,而其他 message 又想使用,那么可以通过 MessageType.EnumType 的方式引用。定义枚举的时候,我们要保证第一个枚举值必须是0,枚举值不能重复,除非使用 option allow_alias = true 选项来开启别名。

enum EnumAllowingAlias {
      option allow_alias = true;
      UNKNOWN = 0;
      STARTED = 1;
      RUNNING = 1;
}

//map类型
map<key_type, value_type> map_field = N;1
其中key_type可以是任意Integer或者string类型(所以,除了floating和bytes的任意标量类型都是可以的)value_type可以是任意类型。

例如,如果你希望创建一个project的映射,每个Projecct使用一个string作为key,你可以像下面这样定义:

map<string, Project> projects = 3;

**

示例:

**
定义xxx.proto

syntax = "proto3";

// 声明是为了防止不同项目之间的命名冲突,编译生成的类将被放置在一个与 package 名相同的命名空间中。
package tutorial;

message Student {
        // 字段编号:消息定义中的每个字段都有一个唯一的编号。这些字段编号用于以二进制格式标识您的字段,一旦您的消息类型被使用,就不应该被更改
        uint64 id = 1;
        string name = 2;
    // singular修饰符修饰的字段可以是0次或者1次。但是当定制协议,用该修饰符修饰的字段都报错
        // singular string email = 3;
        string email = 3;
        enum PhoneType {
                MOBILE         = 0; //proto3版本中,首成员必须为0,成员不应有相同的值
                HOME         = 1;
        }
        message PhoneNumber {
                string number         = 1;
            PhoneType type = 2;
        }
        // repeated: 该字段可以重复任意次数(包括零次)。重复值的顺序将被保留
        repeated PhoneNumber phone = 4;
}

在这里插入图片描述解析出.cc和.h文件

protoc proto文件路径 --cpp_out=C++代码文件导出目录

Protobuf数据写入和读取

#include "test.pb.h"		//解析出来的.h文件
#include "stdio.h"

void sendHeart();
void receHeart(TopMessage* topMessage);
void receHeartResp(TopMessage* topMessage);

/*
** ===================================================================
**     Method      :  sendHeart 
**
**     Description :  数据写入
** 
** ===================================================================
*/
void sendHeart(){
	
	TopMessage message;
	message.set_message_type(REQUEST_HEARTBEAT_SIGNAL);
	printf("sendHeart %d\n",message.message_type());
	receHeart(&message);
}

/*
** ===================================================================
**     Method      :  receHeart 
**
**     Description :  数据读取然后写入
** 
** ===================================================================
*/
void receHeart(TopMessage* topMessage){

	if (topMessage->message_type() == REQUEST_HEARTBEAT_SIGNAL)
	{
		
		printf("request_heartbeat_signal\n");
		TopMessage topMessageResp;

		MsgResult mesResult;
		mesResult.set_result(true);
	
		mesResult.set_error_code("error");
		
		topMessageResp.set_message_type(RESPONSE_HEARTBEAT_RESULT);
		
		*topMessageResp.mutable_msg_result() = mesResult;

		receHeartResp(&topMessageResp);
	}
	
}

/*
** ===================================================================
**     Method      :  receHeartResp 
**
**     Description :  数据读取
** 
** ===================================================================
*/
void receHeartResp(TopMessage* topMessage){

	if (topMessage->message_type() == RESPONSE_HEARTBEAT_RESULT)
	{
		printf("response_heartbeat_result\n");
		
		printf("%s\n",topMessage->msg_result().error_code().c_str());

	}
}

int main()
{
	sendHeart();
	google::protobuf::ShutdownProtobufLibrary();//删除所有已分配的内存(Protobuf使用的堆内存)
}

先进行数据写入,然后数据读取,根据读取到的数据,进行数据写入,最后再读取验证。
整个Demo很简单,包含了数据的读取和写入基本操作
详细的读取和写入方式还请自行查找相关文档或访问官网,我这里由于官网需要翻墙无法访问,且c++水平有限,仅通过解码后的.cc 和 .h进行推测

编译

g++ -std=c++11 test.pb.cc test.cpp -o test `pkg-config --cflags --libs protobuf`

test.pb.cc为解析出来的.cc源文件
test.cpp为编写Demo的源文件
-std=c++11 需要ISO C++ 2011 standard 支持,必须加,否则会报下列错误,编译无法通过

执行

./test
//输出
sendHeart 1
request_heartbeat_signal
response_heartbeat_result
error

//Protobuf API,读写类,编译器为每个字段生成读写函数

  // optional uint64 id = 1;
  void clear_id();
  static const int kIdFieldNumber = 1;
  ::google::protobuf::uint64 id() const;
  void set_id(::google::protobuf::uint64 value);

  // optional string name = 2;
  void clear_name();
  static const int kNameFieldNumber = 2;
  const ::std::string& name() const;
  void set_name(const ::std::string& value);
  void set_name(const char* value);
  void set_name(const char* value, size_t size);
  ::std::string* mutable_name();
  ::std::string* release_name();
  void set_allocated_name(::std::string* name);

  // optional string email = 3;
  void clear_email();
  static const int kEmailFieldNumber = 3;
  const ::std::string& email() const;
  void set_email(const ::std::string& value);
  void set_email(const char* value);
  void set_email(const char* value, size_t size);
  ::std::string* mutable_email();
  ::std::string* release_email();
  void set_allocated_email(::std::string* email);

  // repeated .tutorial.Student.PhoneNumber phone = 4;
  int phone_size() const;
  void clear_phone();
  static const int kPhoneFieldNumber = 4;
  const ::tutorial::Student_PhoneNumber& phone(int index) const;
  ::tutorial::Student_PhoneNumber* mutable_phone(int index);
  ::tutorial::Student_PhoneNumber* add_phone();
  ::google::protobuf::RepeatedPtrField< ::tutorial::Student_PhoneNumber >* mutable_phone();
  const ::google::protobuf::RepeatedPtrField< ::tutorial::Student_PhoneNumber >&phone() const;

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1304487.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【WinRAR】为什么右键没有压缩选项?

我们安装了WinRAR之后想要压缩文件&#xff0c;但是右键点击文件之后发现并没有WinRAR压缩选项&#xff0c;这应该如何设置才能出现右键带有压缩选项呢&#xff1f;方法如下&#xff1a; 首先打开WinRAR&#xff0c;在上面功能中点击选项 – 设置 然后我们在设置界面中切换到集…

Guardrails for Amazon Bedrock 基于具体使用案例与负责任 AI 政策实现定制式安全保障(预览版)

作为负责任的人工智能&#xff08;AI&#xff09;战略的一部分&#xff0c;您现在可以使用 Guardrails for Amazon Bedrock&#xff08;预览版&#xff09;&#xff0c;实施专为您的用例和负责任的人工智能政策而定制的保障措施&#xff0c;以此促进用户与生成式人工智能应用程…

parser

"typescript-eslint/parser": "5.56.0", "vue-eslint-parser": "9.1.0", 代码来自ruoyi-plus vue-eslint-parser是一个专门用于解析Vue.js单文件组件&#xff08;.vue文件&#xff09;的ESLint插件。ESLint是一个用于检查和修复Java…

陪诊软件开发|北京陪诊系统功能介绍

随着互联网技术的不断发展&#xff0c;医疗行业也日新月异。在这个数字化时代&#xff0c;陪诊软件成为了重要的创新工具&#xff0c;为患者和医生提供了全新的医疗服务体验。本文将探讨陪诊软件开发的关键要点及其功能&#xff0c;为您揭示医疗行业的又一面向未来的重要突破口…

【Linux】线程的概念理解,从感知理解到全面深入

1.初始线程概念 在伟大的”计算机哲学“操作系统这本书中&#xff0c;一般给出线程的概念为&#xff1a;是在进程内部运行的一个执行分支&#xff08;执行流&#xff09;&#xff0c;属于进程的一部分&#xff0c;粒度要比进程更加细腻和轻量化。大家对这一概念一看而过既可以…

高频交易双柜台系统如何开通?

市面上的量化系统很多&#xff0c;快速通道也很多&#xff0c;但是可以满足双柜台系统的这个确实是很少的。能满足双柜台系统的&#xff0c;那恐怕只有华锐双柜台系统可以&#xff01;我们先简单来了解下双柜台系统&#xff1a; 华锐双柜台系统&#xff08;SST&#xff09;是…

外贸行业怎么跟进客户?

外贸行业怎么跟进客户&#xff1f; 建立一个完善的跟进系统需要人性化关怀和高效的技术支持。 人性化关怀是指以客户为中心&#xff0c;关注客户的需求和情感&#xff0c;建立真诚、个性化的关系&#xff1b;高效的技术支持则是指利用技术和工具来提高跟进效率和服务质量&#…

QT用户管理效果预览

用户管理效果 QT 用户管理 GITHUB网站 QT版本 CMakeList.txt find_package(Qt${QT_VERSION_MAJOR} COMPONENTS Sql REQUIRED) target_link_libraries(tableView PRIVATE Qt${QT_VERSION_MAJOR}::Widgets Qt${QT_VERSION_MAJOR}::Sql)GitHub代码获取链接 GitHub代码获取链接

《码农的噩梦与修电脑的奇幻之旅》

故事从一个充满梦想的码农学习计算机编程开始。他对编写程序充满了热情&#xff0c;认为自己就像是一位能够编织魔法的巫师&#xff0c;能够创造出炫酷的虚拟世界。 然而&#xff0c;这个充满幻想的故事在码农入门的第一天就遭遇了突如其来的挫折。电脑故障了&#xff01;所有…

专栏十五:omicverse在单细胞分析中的实际使用体验和小改动

写一些自己的理解吧,一些小步骤,正在更新中。。。 安装 原作者写的很清楚了 大部分直接抄 个别地方:去选择下载适合自己的pytorch版本PyTorch, 比如我的是cuda12,下载命令实际是 pip3 install torch torchvision torchaudio 查看cuda版本命令 nvidia-smi 当然还有个命…

【GIS】1.什么是空间分析?

新坑&#xff01;&#xff01;&#xff01; 本系列文章主要参考CHICAGO大学的空间数据科学中心的相关课程(&#xffe3;∇&#xffe3;)/感谢分享&#xff5e; 什么是空间分析 空间分析不仅仅是指做一个地图这么简单&#xff0c;而是为这些位置空间添加实际的值&#xff0c;并…

thinkphp商城系统之商品模块表设计

5.后台商品模块 商品模块是商城项目最核心的模块&#xff0c;也是最复杂的。如果说你能掌握这个模块&#xff0c;那对你来说绝对是获益匪浅&#xff0c;这模块会涉及到以下知识点&#xff1a; 聊聊电商领域中的SPU和SKU表的设计&#xff0c;告诉你为什么要这么设计复杂的查询…

实现 wsl ssh 自启动

目录 一、wsl 端操作1.1 创建并编辑 /etc/init.wsl&#xff0c;加入如下内容&#xff0c;并保存1.2 对文件 /etc/init.wsl 添加执行权限1.3 编辑sudoers&#xff0c;避免输入密码 二、windows 端操作2.1 新建 startservice.vbs 文件&#xff0c;并保存如下内容2.2 实现脚本 sta…

前端使用XLSX导出excel表格

1 单个sheet page.js(页面中的导出方法) import { exportExcel } from ../../../utils/exportExcel.js; leadOut() {const arr [{ id: 1, name: 张三, age: 14, sex: 男 },{ id: 2, name: 李四, age: 15, sex: 女 },{ id: 3, name: 王五, age: 16, sex: 男 },];const allR…

MySQL数据恢复之binlog2sql的安装和使用,很详细

MySQL数据恢复之binlog2sql的安装和使用&#xff0c;很详细 一、前言二、binlog2sql的介绍三、安装binlog2sql1、安装git&#xff08;已安装可以跳过&#xff09;&#xff08;1&#xff09;、正常安装&#xff08;2&#xff09;、编译安装报错①、安装libcurl &#xff08;2&am…

Win11专业版,eNSP启动失败,错误代码40 解决方法

微软Win11系统默认开启的 Virtualization-based Security &#xff08;VBS&#xff09;“基于虚拟化的安全性”会导致游戏、跑分性能下降。VBS 基于虚拟化的安全性&#xff0c;通常称为内核隔离。使用硬件虚拟化在内存中创建安全区域&#xff0c;为其他安全功能提供了一个安全平…

DNSLog漏洞探测(五)之XXE漏洞实战

DNSLog漏洞探测(五)之XXE漏洞实战 本文我们来学习如何利用DNSLog平台探测目标网站是否存在RCE漏洞&#xff0c;接下来我们还是利用Pikachu的靶场作为演示。首先我们先进入Pikachu靶场的XXE漏洞界面。 如果此时网站的界面存在着无回显的XXE漏洞&#xff0c;这种情况下&#xff…

JMeter直连数据库

JMeter直连数据库 使用场景操作步骤 使用场景 用作请求的参数化 登录时需要的用户名&#xff0c;密码可以从数据库中查询获取 用作结果的断言 添加购物车下订单&#xff0c;检查接口返回的订单号&#xff0c;是否与数据库中生成的订单号一致 清理垃圾数据 添加商品后&#xff…

美团P3-2大佬写给初中级前端的《高级进阶面试指南》

都说金9银10&#xff0c;节前自己也去面试了几家公司&#xff0c;幸而都收到了offer。如今已经入职美团两月有余&#xff0c;最近闲来有空&#xff0c;整理一番&#xff0c;希望对各位找工作有帮助。 说在前面&#xff0c;我的答案仅供参考。答案有不全或有偏颇之处&#xff0…

Linux 搭建 gitlab

目录 前言安装依赖项添加GitLab存储库安装GitLab CE创建新存储目录编辑GitLab配置文件,例如更改默认域名或端口:重新配置并启动GitLab服务以应用更改:前言 centos搭建gitlab代码仓库 安装依赖项 在安装GitLab之前,您需要先安装一些必要的依赖项: yum install -y curl …