【ProtoBuf】使用指南

一.什么是ProtoBuf

特点:ProtoBuf是用于序列化和反序列化的一种方法,类似xml和json,但是效率更高,体积更小。ProtoBuf具有语⾔⽆关、平台⽆关,扩展性、兼容性好等特点。

ProtoBuf是需要依赖通过编译生成的头文件和源文件来使用的。也就是说他需要参与到编译链接的过程中。

使用方法简单来说,我们之需要写一个包含待处理数据的message,就可以通过protoc编译器编译.proto文件自动生成接口,在后续业务逻辑中我们直接使用这些接口即可。

二.形成proto文件

在protobuf里面定义的结构化对象叫message,在这个结构化对象中定义其对应的属性内容。在这里我们使用proto3语法,需要手动指定。同时指定包(类比命名空间)。

定义消息字段:字段类型+字段名=字段唯⼀编号。

这里的字段类型和c++一致(在类型后面带上位数)。字段唯⼀编号是⽤来标识字段,⼀旦开始使⽤就不能够再改变。但注意,这里有可能存在变长编码,变⻓编码是指:经过protobuf编码后,原本4字节或8字节的数可能会被变为其他字节数。

通过以上知识就能得到如下用例。

syntax = "proto3";  //指定语法
package contacts;   //命名空间message StudentInfo
{string name = 1;  // 姓名int age = 2;    // 年龄  
}

字段唯⼀编号的范围:1~536,870,911(2^29-1),其中19000~19999不可⽤。范围为1~15的字段编号需要⼀个字节进⾏编码,16~2047内的数字需要两个字节进⾏编码。编码后的字节不仅只包含了编号,还包含了字段类型。1~15要⽤来标记出现⾮常频繁的字段。

接下来编译生成C++文件
编译命令格式:protoc [--proto_path=IMPORT_PATH] --cpp_out=DST_DIR path/to/file.proto

【】内可以省略

在这里就可以是protoc --cpp_out=. contacts.proto 

编译后⽣成了两个⽂件: contacts.pb.h  contacts.pb.cc,

对于编译⽣成的C++代码,包含了以下内容:

  • 对于每个message,都会⽣成⼀个对应的消息类。
  • 在消息类中,编译器为每个字段提供了获取和设置⽅法
  • 编辑器会针对于每个.proto ⽂件⽣成 .h 和 .cc ⽂件,分别⽤来存放类的声明与类的实现
     

这样就能得到基本的操作接口,类似这种 

接下来就可以使用序列化和反序列化了在消息类的⽗类中能找到MessageLite,这里提供了序列化反序列化的接口。

//序列化:
bool SerializeToOstream(ostream* output) const; // 将序列化后数据写⼊⽂件
流
bool SerializeToArray(void *data, int size) const;
bool SerializeToString(string* output) const;
//反序列化:
bool ParseFromIstream(istream* input); // 从流中读取数据,再进⾏反序列化
动作
bool ParseFromArray(const void* data, int size);
bool ParseFromString(const string& data);

这样就能把数据结构转换为二进制结构。详情可参考protobuf官网。这样就能实现从序列化到反序列的整体逻辑,注意在编译的时候要告知编译器所使用的库名称。

三.详细语法
 

一.字段规则

  • singular:消息中可以包含该字段零次或⼀次(不超过⼀次)。proto3语法中,字段默认使⽤该规则
  • repeated:消息中可以包含该字段任意多次(包括零次),其中重复值的顺序会被保留。可以理解为定义了⼀个数组
    syntax = "proto3";
    package contacts;message StudentInfo 
    {
    string name = 1;
    int32 age = 2;
    repeated string phone_numbers = 3;    //能有多个电话
    }
    
     

二.使用消息字段

proto里的消息体是可以重复嵌套的。

同时消息类型是可以充当字段来使用的。

syntax = "proto3";
package contacts;message StudentInfo 
{string name = 1;int32 age = 2;message Phone{string number = 1;}repeated Phone phone = 3;}

我们也可以在一个proto内部导入其他proto的消息类型

//使用 import 导入其他类型的proto文件
import "phone.proto"; // 引⼊的⽂件声明了package,使⽤消息时,需要⽤ ‘命名空间.消息类型’ 格式message test
{phone.Phone phone = 1;
}

注意在使用api对消息类型进行赋值的时候有两个接口,mutable_⽅法,返回值为消息类型的指针,这类方法会为我们开辟好空间,可以直接对这块空间的内容进⾏修改。或用 set_allocated_这个函数需要手动传入一个你自己开辟好的空间。

三.enum 类型

要注意枚举类型的定义有以下几种规则:
1. 0值常量必须存在,且要作为第⼀个元素。这是为了与proto2的语义兼容:第⼀个元素作为默认
值,且值为0。
2. 枚举类型可以在消息外定义,也可以在消息体内定义(嵌套)。
3. 枚举的常量值在32位整数的范围内。但因负值⽆效因⽽不建议使⽤(与编码规则有关)

同时具有相同枚举值名称不能出现在同一级别下。同级(同层)的枚举类型,各个枚举类型中的常量不能重名。
• 单个.proto⽂件下,最外层枚举类型和嵌套枚举类型,不算同级。
• 多个.proto⽂件下,若⼀个⽂件引⼊了其他⽂件,且每个⽂件都未声明package,每个proto⽂
件中的枚举类型都在最外层,算同级。
• 多个.proto⽂件下,若⼀个⽂件引⼊了其他⽂件,且每个⽂件都声明了package,不算同级。
 

在使用枚举类型的时候使用诸如set_,或者type,用来设置和获取枚举类型

四.Any类型

Any类型可以看成c++的auto泛型类型,使⽤时可以在Any中存储任意消息类型,Any类
型的字段也⽤repeated来修饰。注意在使用的时候引入any.proto

import "google/protobuf/any.proto";message test
{google.protobuf.Any data = 1;
}

对于any类型来说,设置方法可以用mutable方法修改。

any类型可以和普通类型之间可以互相转换,使用PackFrom() 方法可以将任意消息类型转为 Any 类型。使⽤ UnpackTo() ⽅法可以将 Any 类型转回之前设置的任意消息类型。使用 Is() 方法可以⽤来判断存放的消息类型是否为 typename T。

五.oneof类型

表示这其中的字段同时只有一个字段会被设置。同时oneof里不能设置repeated,若是在oneof里多次设置,则会保留最后一次设置字段属性,可以用诸如_case方法获取设置了哪一个字段

六.map类型

可以类比c++中的map类型

map<key_type, value_type> map_field = N;

注意key值不能是float或者double.使用map方法也用mutable方法进行设置

四.语法的细节处理

  •  对于字符串,默认值为空字符串。
  •  对于字节,默认值为空字节。
  •  对于布尔值,默认值为false。
  •  对于数值类型,默认值为0。、
  • 对于枚举,默认值是第⼀个定义的枚举值,必须为0。
  • 于设置了repeated的字段的默认值是空的(通常是相应语⾔的⼀个空列表)
  • 对于 消息字段 、 oneof字段 和 any字段 ,C++和Java语⾔中都有has_⽅法来检测当前字段
    是否被设置
     

更新消息字段:

  • 禁⽌修改任何已有字段的字段编号。
  • 若是移除⽼字段,要保证不再使⽤移除字段的字段编号。正确的做法是保留字段编号(reserved),以确保该编号将不能被重复使⽤。不建议直接删除或注释掉字段。
  • int32,uint32,int64,uint64和bool是完全兼容的。可以从这些类型中的⼀个改为另⼀个,⽽不破坏前后兼容性。若解析出来的数值与相应的类型不匹配,会采⽤与C++⼀致的处理⽅案(例如,若将64位整数当做32位进⾏读取,它将被截断为32位)。
  • sint32和sint64相互兼容但不与其他的整型兼容。
  •  string和bytes在合法UTF-8字节前提下也是兼容的。
  •  fixed32与sfixed32兼容,fixed64与sfixed64兼容。
  • oneof:
    ◦ 将⼀个单独的值更改为新oneof类型成员之⼀是安全和⼆进制兼容的。
    ◦ 若确定没有代码⼀次性设置多个值那么将多个字段移⼊⼀个新oneof类型也是可⾏的。
    ◦ 将任何字段移⼊已存在的oneof类型是不安全的。

 如果删除一个字段后用新的字段占据了这个编号,在反序列化的时候就会解析出错误的信息。

若是新设置了字段,但却仍然使用就的方法序列化的话,新增的字段在旧程序中其实并没有丢失,⽽是会作为旧程序的未知字段。

未知字段:

在了解未知字段之前先需要知道一下protobuf之间不同类的关系。

MessageLite仅仅提供序列化、反序列化功能,跟message属于同一个层级,是message类的拓展。

Descriptor类是描述和管理message属性的类,是message类的下层。

Reflection主要提供了动态读写消息字段的接⼝,对消息对象的⾃动读写主要通过该类完成。提供⽅法来动态访问/修改message中的字段,对每种类型,Reflection都提供了⼀个单独的接⼝⽤于读写字段对应的值。类中还包含了访问/修改未知字段的⽅法。是message类的下层

UnknownFieldSet类
包含在分析消息时遇到但未由其类型定义的所有字段。是Reflection类的下层

UnknownField类

表⽰未知字段集中的⼀个字段,是UnknownFieldSet的下层。这里是未知字段的类型

enum Type {
TYPE_VARINT,
TYPE_FIXED32,
TYPE_FIXED64,
TYPE_LENGTH_DELIMITED,
TYPE_GROUP
};

当我们想拿到未知字段需要一层一层获取它的上层对象

//这里以people的字段为例
const Reflection* reflection = PeopleInfo::GetReflection();
const UnknownFieldSet& unknowSet = reflection->GetUnknownFields(people);for (int j = 0; j < unknowSet.field_count(); j++)
{const UnknownField& unknow_field = unknowSet.field(j);
}

五.选项option

.proto⽂件中可以声明许多选项,使⽤option 标注。选项能影响proto编译器的某些处理⽅式。


常用选项列举
 

 optimize_for:

该选项为文件选项,可以设置protoc编译器的优化级别,分别为SPEED 、
CODE_SIZE 、 LITE_RUNTIME 。受该选项影响,设置不同的优化级别,编译.proto⽂件后⽣
成的代码内容不同

SPEED :protoc编译器将⽣成的代码是⾼度优化的,代码运⾏效率⾼,但是由此⽣成的代码编译后会占⽤更多的空间。 SPEED 是默认选项
CODE_SIZE :proto编译器将⽣成最少的类,会占⽤更少的空间,是依赖基于反射的代码来
实现序列化、反序列化和各种其他操作。但和 SPEED 恰恰相反,它的代码运⾏效率较低。这
种⽅式适合⽤在包含⼤量的.proto⽂件,但并不盲⽬追求速度的应⽤中。

LITE_RUNTIME :⽣成的代码执⾏效率⾼,同时⽣成代码编译后的所占⽤的空间也是⾮常
少。这是以牺牲ProtocolBuffer提供的反射功能为代价的,仅仅提供encoding+序列化功能,
所以我们在链接库时仅需链接libprotobuf-lite,⽽⾮libprotobuf。这种模式通常⽤于资源
有限的平台,例如移动⼿机平台中
 

 option optimize_for = SPEED;

allow_alias:

 允许将相同的常量值分配给不同的枚举常量,⽤来定义别名。该选项为枚举选项。

 enum TestType

{
        option allow_alias = true;
        test1 = 0;
        test2= 1;
        test3 = 1; // 若不加 option allow_alias = true; 这⼀⾏会编译报错
}

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

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

相关文章

oracle 12 查询数据库锁

在Oracle 12c中&#xff0c;查询数据库锁信息可以通过以下视图进行&#xff1a; v$locked_object&#xff1a;这个视图显示了当前被锁定的对象&#xff08;如表、行等&#xff09;的信息。 SELECT l.session_id sid, s.serial#, l.locked_mode,o.object_name,s.osuser,s.userna…

1.25学习总结

今天学习了二叉树&#xff0c;了解了二叉树的创建和遍历的过程 今天所了解的遍历过程主要分为三种&#xff0c;前序中序和后序&#xff0c;都是DFS的想法 前序遍历&#xff1a;先输出在遍历左节点和右节点&#xff08;输出->左->右&#xff09; 中序遍历&#xff1a;先…

网络工程师:新兴科技基础知识面试题(十四)

116.简述模拟信号到数字信号是怎么转化的。 模拟信号到数字信号的转化过程是通过模数转换器&#xff08;ADC&#xff09;完成的&#xff0c;该过程也被称为采样和量化。 1. 采样&#xff1a; 模拟信号是连续的&#xff0c;而数字信号是离散的。在转换过程中&#xff0c;模拟信…

ip数据库.

IP库(也叫IP地址数据库)&#xff0c;是由专业技术人员经过长时间通过多种技术手段收集而来的&#xff0c;并且长期有专业人员进行更新、维护、补充。 IP库里面存放了大量的IP地址&#xff0c;方便用户查询&#xff0c;比如&#xff0c;你知道了某个IP&#xff0c;就可以查到这…

华为云磁盘性能指标(参考)

MD[华为云磁盘性能指标(参考)] 云硬盘&#xff08;Elastic Volume Service, EVS&#xff09; 根据性能&#xff0c;磁盘可分为极速型SSD V2、极速型SSD、通用型SSD V2、超高IO、通用型SSD、高IO、普通IO。 性能指标(参考)&#xff0c;测速说明&#xff1a;操作系统-windows …

如何配置Tomcat服务环境并实现无公网ip访问本地站点

文章目录 前言1.本地Tomcat网页搭建1.1 Tomcat安装1.2 配置环境变量1.3 环境配置1.4 Tomcat运行测试1.5 Cpolar安装和注册 2.本地网页发布2.1.Cpolar云端设置2.2 Cpolar本地设置 3.公网访问测试4.结语 前言 Tomcat作为一个轻量级的服务器&#xff0c;不仅名字很有趣&#xff0…

conda环境下使用youtube_dl模块下载youtube视频异常解决方法

1 问题描述 使用python的youtube_dl模块下载youtube视频时失败&#xff0c;错误信息如下&#xff1a; ERROR: Unable to extract uploader id; please report this issue on https://yt-dl.org/bug . Make sure you are using the latest version; see https://yt-dl.org/up…

LeeCode 438.找到字符串中所有字母异位词.

给定两个字符串 s 和 p&#xff0c;找到 s 中所有 p 的 异位词 的子串&#xff0c;返回这些子串的起始索引。不考虑答案输出的顺序。 异位词 指由相同字母重排列形成的字符串&#xff08;包括相同的字符串&#xff09;。 示例 1: 输入: s “cbaebabacd”, p “abc” 输出:…

jquery练习

jquery练习 1.多图片展示 <!doctype html> <html> <head> <meta charset"utf-8"> <title>多图片展示效果</title> <link href"zns_style.css" rel"stylesheet" type"text/css" /> <sc…

从 Apk 提取代码到单独 dex

从 Apk 提取代码到单独 dex Android 中动态加载是指应用程序在运行时加载和执行 Dex 文件的过程&#xff0c;可以在运行时加载不同的代码或功能&#xff0c;而无需重新编译整个应用程序&#xff0c;动态加载 Dex 文件通常涉及以下步骤&#xff1a; 创建 Dex 文件 我们接触到的…

Redis的五种常用数据类型详解及相关面试问题

目录 Redis的五种常用数据类型详解 简述 Redis五种基本数据类型 String字符串 常用命令 应用场景 Hash散列表 常用命令 使用场景 List链表 常用命令 应用场景 Set( 集合) 常用命令 应用场景 SortedSet( 有序集合) zset 常用命令介绍 应用场景 面试题常问的数…

【Pytorch 第一讲】 如何加载预训练模型

一. 封装Pytorch的Model 加载pre-trianed Model import torch import torchvision.models as models from torchvision import transforms# 1. 下载并加载预训练模型 model models.resnet18(pretrainedFalse) # 设置pretrainedFalse&#xff0c;表示不加载预训练权重# 2. 下载…

退货通知单下推销售退货单,无法下推问题排查

文章目录 退货通知单下推销售退货单&#xff0c;无法下推问题排查报错界面排查原因 退货通知单下推销售退货单&#xff0c;无法下推问题排查 报错界面 排查 检验单已做。 原因 合格未勾选判退。

按键协管指南针加速计陀螺仪GPS等原理图纸2

1.imu电路。 加速计包含重力感应。 到传感器芯片u8, U16, U18的信息都是用的spi接口&#xff0c;如下图所示。OSCAR_TO_IMU_SPI_SCLK_FL, IMU_TO_OSCAR_SPI_MISO_FL, OSCAR_TO_IMU_SPI_MOSI_FL接了u8, u16, u18,通过片选信号cs选择哪个芯片接收。 加速计&#xff0c;陀螺仪&…

Redash 默认key漏洞(CVE-2021-41192)复现

Redash是以色列Redash公司的一套数据整合分析解决方案。该产品支持数据整合、数据可视化、查询编辑和数据共享等。 Redash 10.0.0及之前版本存在安全漏洞&#xff0c;攻击者可利用该漏洞来使用已知的默认值伪造会话。 1.漏洞级别 中危 2.漏洞搜索 fofa "redash"…

289. 生命游戏

根据 百度百科 &#xff0c; 生命游戏 &#xff0c;简称为 生命 &#xff0c;是英国数学家约翰何顿康威在 1970 年发明的细胞自动机。 给定一个包含 m n 个格子的面板&#xff0c;每一个格子都可以看成是一个细胞。每个细胞都具有一个初始状态&#xff1a; 1 即为 活细胞 &am…

【ASP.NET Core 基础知识】--数据库连接--使用Entity Framework Core进行数据库访问

一、介绍 Entity Framework Core&#xff08;简称EF Core&#xff09;是微软推出的一个轻量级版的Entity Framework&#xff0c;它是一个开源的、跨平台&#xff08;Windows、Linux和macOS&#xff09;的对象关系映射&#xff08;ORM&#xff09;框架。EF Core 旨在提供快速的…

主播考核体系相关基础

1.主播薪资类型 2.主播考核体系 1.分为日常考核、月度考核 日常考核分为三部曲&#xff1a;播前、播中、播后 &#xff08;1&#xff09;播前 &#xff08;2&#xff09;播中 &#xff08;3&#xff09;播后 月度考核 月度考核表列举 主播等级划分要素 主播晋升考核方…

JVM篇----第六篇

系列文章目录 文章目录 系列文章目录前言一、堆(Heap-线程共享) -运行时数据区二、方法区/永久代(线程共享)三、JVM 运行时内存四、新生代前言 前些天发现了一个巨牛的人工智能学习网站,通俗易懂,风趣幽默,忍不住分享一下给大家。点击跳转到网站,这篇文章男女通用,看…

Java面试题(5)

22.Iterator如何使用&#xff0c;有什么特点 java中的Iterator功能比较见到你&#xff0c;并且只能单向移动 1. iterator()&#xff1a;要求容器返回一个Iterator 2. next():用于获取序列的下一个元素&#xff08;第一次调用获取第一个元素&#xff09; 3. hasNext()&#xff…