ProtoBuf的使用

目录

1.创建.proto文件

1.1文件规范

1.2添加注释

1.3指定proto3语法

1.4package声明符

1.5定义消息(message)

1.6定义消息字段

2.编译contacts.proto文件

3.序列化与反序列化的使用


1.创建.proto文件

1.1文件规范

• 创建.proto文件时,⽂件命名应该使用全小写字母命名,多个字⺟之间⽤ _ 连接。例如:
lower_snake_case.proto 。
• 书写.proto⽂件代码时,应使用2个空格的缩进。

1.2添加注释

向⽂件添加注释,可使⽤ // ?或者? /* ... */ ?

1.3指定proto3语法

Protocol Buffers语⾔版本3,简称proto3,是.proto文件最新的语法版本。proto3简化了Protocol
Buffers语言,既易于使用,又可以在更⼴泛的编程语言中使⽤。它允许你使用Java,C++,Python等多种语言生成protocol buffer代码。
在.proto文件中,要使用 syntax = "proto3"; 来指定⽂件语法为proto3,并且必须写在除去注释内容的第⼀行。如果没有指定,编译器会使⽤proto2语法。
在通讯录1.0的contacts.proto文件中,可以为文件指定proto3语法,内容如下:

syntax = "proto3"; 

1.4package声明符

package是一个可选的声明符,能表示.proto文件的命名空间,在项目中要有唯一性。它的作用是为了避免我们定义的消息出现冲突。
在通讯录1.0的contacts.proto文件中,可以声明其命名空间,内容如下:

syntax = "proto3";
package contacts;

1.5定义消息(message)

消息(message) : 要定义的结构化对象,我们可以给这个结构化对象中定义其对应的属性内容。
这里再提一下为什么要定义消息?
在网络传输中,我们需 要为传输双方定制协议。定制协议说白了就是定义结构体或者结构化数据,
比如,tcp, udp 报文就是结构化的。
再比如将数据持久化存储到数据库时,会将一系列元数据统一用对象组织起来,再进行存储。
所以ProtoBuf就是以message的方式来支持我们定制协议字段,后期帮助我们形成类和方法来使
用。在通讯录1.0中我们就需要为联系人定义一个message。
.proto文件中定义一个消息类型的格式为:

message 消息类型名{
}

消息类型命名规范:使用驼峰命名法,首字母⼤写。

为contacts.proto (通讯录1.0)新增联系人message, 内容如下:

syntax = "proto3";
package contacts;
// 定义联系⼈消息
message PeopleInfo {
}

1.6定义消息字段

在message中我们可以定义其属性字段,字段定义格式为:字段类型字段名=字段唯一编号 ;
●字段名称命名规范:全小写字母,多个字母之间用_连接。
●字段类型分为:标量数据类型和特殊类型(包括枚举、其他消息类型等)。
●字段唯一编号:用来标识字段,一旦开始使用就不能够再改变。

该表格展示了定义于消息体中的标量数据类型,以及编译.proto文件之后自动生成的类中与之对应的字段类型。在这里展示了与C++语言对应的类型。

[1]变长编码是指:经过protobuf 编码后,原本4字节或8字节的数可能会被变为其他字节数。

更新contacts.proto (通讯录1.0),新增姓名、年龄字段:

syntax = "proto3";
package contacts;message PeopleInfo {string name = 1;int32 age = 2;
}

在这里还要特别讲解一下字段唯一 编号的范围:
1~ 536,870,911 (2^29-1),其中19000 ~ 19999不可用。
19000 ~ 19999不可用是因为:在Protobuf协议的实现中,对这些数进行了预留。如果非要在.proto
文件中使用这些预留标识号,例如将name字段的编号设置为19000,编译时就会报警:

// 消息中定义了如下编号,代码会告警:
// Field numbers 19,000 through 19,999 are reserved for the protobuf implementation
string name = 19000;

值得一提的是,范围为1~ 15的字段编号需要-一个字节进行编码,16 ~ 2047内的数字需要两个字节进行编码。编码后的字节不仅只包含了编号,还包含了字段类型。所以1 ~ 15要用来标记出现非常频繁的字段,要为将来有可能添加的、频繁出现的字段预留一些出来。

2.编译contacts.proto文件

编译命令
编译命令行格式为:

编译contacts.proto文件命令如下:

protoc --cpp_out=. contacts.proto

编译contacts.proto文件后,会生成所选择语言的代码,我们选择的是C++,所以编译后生成了两个
文件: contacts.pb.hI contacts.pb.cc。
对于编译生成的C++代码,包含了以下内容:
●对于每个message,都会生成一个对应的消息类。
●在消息类中,编译器为每个字段提供了获取和设置方法,以及一下其他能够操作字段的方法。
●编辑器会针对于每个.proto文件生成.h和.cc文件,分别用来存放类的声明与类的实现。
contacts.pb.h部分代码展示

class PeopleInfo final : public ::PROTOBUF_NAMESPACE_ID::Message
{
public:using ::PROTOBUF_NAMESPACE_ID::Message::CopyFrom;void CopyFrom(const PeopleInfo &from);using ::PROTOBUF_NAMESPACE_ID::Message::MergeFrom;void MergeFrom(const PeopleInfo &from){PeopleInfo::MergeImpl(*this, from);}static ::PROTOBUF_NAMESPACE_ID::StringPiece FullMessageName(){return "PeopleInfo";}// string name = 1;void clear_name();const std::string &name() const;template <typename ArgT0 = const std::string &, typename... ArgT>void set_name(ArgT0 &&arg0, ArgT... args);std::string *mutable_name();PROTOBUF_NODISCARD std::string *release_name();void set_allocated_name(std::string *name);// int32 age = 2;void clear_age();int32_t age() const;void set_age(int32_t value);
};

上述的例子中:
● 每个字段都有设置和获取的方法,getter的名称与小写字段完全相同,setter方法以set_ 开头。
● 每个字段都有一个clear_ 方法,可以将字段重新设置回empty状态。

contacts.pb.cc中的代码就是对类声明方法的一些实现,在这里就不展开了。
到这里有同学可能就有疑惑了,那之前提到的序列化和反序列化方法在哪里呢?在消息类的父类
MessageLite中,提供了读写消息实例的方法,包括序列化方法和反序列化方法。

class MessageLite {public://序列化: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);
};

注意:
●序列化的结果为二进制字节序列,而非文本格式。
●以上三种序列化的方法没有本质上的区别,只是序列化后输出的格式不同,可以供不同的应用场景使用。
●序列化的API函数均为const成员函数,因为序列化不会改变类对象的内容,而是将序列化的结果
保存到函数入参指定的地址中。

3.序列化与反序列化的使用

创建一个测试文件main.cc,方法中我们实现:
●对一个联系人的信息使用PB进行序列化,并将结果打印出来。
●对序列化后的内容使用PB进行反序列,解析出联系人信息并打印出来。

main.cc

#include <iostream>
#include "contacts.pb.h"
using namespace std;int main()
{string people_str;{//.proto文件声明的package,通过protoc编译后,会为编译生成的C++代码声明同名的命名空间//范围是在.proto文件中定义的内容contacts::PeopleInfo people;people.set_age(20);people.set_name("张三");//调用序列化的方法,将序列化后的二进制序列存放到string中if(!people.SerializeToString(&people_str)) {cout << "序列化联系人失败" << endl;}//打印序列化的结果:cout << "序列化的结果:" << people_str << endl;}{contacts::PeopleInfo people;//调用反序列化方法,读取string中存放的二进制序列,并反序列化出对象if(!people.ParseFromString(people_str)) {cout << "反序列化联系人失败" << endl;}//打印结果:cout << "联系人年龄:" << people.age() << endl; cout << "联系人姓名:" << people.name() << endl; }return 0;
}

代码书写完成后,编译main.cc,生成可执行程序TestProtoBuf :

g++ main.cc contacts.pb.cc -o TestProtoBuf -std=c++11 -lprotobuf 

●-lprotobuf: 必加,不然会有链接错误。
●-std=c++11: 必加,使用C++11语法。

执行TestProtoBuf,可以看见people经过序列化和反序列化后的结果: 

由于ProtoBuf是把联系人对象序列化成了二进制序列,这里用string来作为接收二进制序列的容器。
所以在终端打印的时候会有换行等一些乱码显示。
所以相对于xml和JSON来说,因为被编码成二进制,破解成本增大,ProtoBuf 编码是相对安全的。

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

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

相关文章

利用互斥锁解决缓存击穿问题

核心思路&#xff1a;相较于原来从缓存中查询不到数据后直接查询数据库而言&#xff0c;现在的方案是 进行查询之后&#xff0c;如果从缓存没有查询到数据&#xff0c;则进行互斥锁的获取&#xff0c;获取互斥锁后&#xff0c;判断是否获得到了锁&#xff0c;如果没有获得到&am…

DNS如何在Windows NIC配置多个DNS服务器时完成DNS解析查询

使用多个 DNS 服务器 IP 配置 DNS 客户端会增加 DNS 基础结构的容错。所以建议给Windows客户端使用多个 DNS 服务器。 文章目录 什么是DNS&#xff1f;在 NIC 上配置单个 DNS 服务器时&#xff0c;DNS 客户端的解析过程在 NIC 上配置两个 DNS 服务器时&#xff0c;DNS 客户端的…

【Docker】从零开始:3.Docker运行原理

【Docker】从零开始&#xff1a;3.Docker运行原理 Docker 工作原理Docker与系统的关系Docker平台架构图解 Docker 工作原理 Docker与系统的关系 Docker 是一个 Client-Server 结构的系统&#xff0c;Docker 守尹进程运行在王机上&#xff0c; 然后通过 Socket 连接从各尸端坊…

Vue3-shallowRef 和 shallowReactive函数(浅层次的响应式)

Vue3-shallowRef 和 shallowReactive函数&#xff08;浅层次的响应式&#xff09; shallowRef函数 功能&#xff1a;只给基本数据类型添加响应式。如果是对象&#xff0c;则不会支持响应式&#xff0c;层成也不会创建Proxy对象。ref和shallowRef在基本数据类型上是没有区别的…

第1关:图的邻接表存储及求邻接点操作

任务要求参考答案评论2 任务描述相关知识编程要求测试说明 任务描述 本关任务&#xff1a;要求从文件输入顶点和边数据&#xff0c;包括顶点信息、边、权值等&#xff0c;编写程序实现以下功能。 1&#xff09;构造图G的邻接表和顶点集&#xff0c;即图的存储结构为邻接表。 …

SpringCloud原理-OpenFeign篇(二、OpenFeign包扫描和FeignClient的注册原理)

文章目录 前言正文一、从启动类开始二、EnableFeignClients 的源码分析三、Import FeignClientsRegistrar 的作用四、FeignClientsRegistrar#registerFeignClients(...)五、饥饿注册&懒注册 FeignClientsRegistrar#registerFeignClient(...)六、通过Holder真正注册beanDefi…

SSM框架(一):Spring 容器

文章目录 一、Spring Framework系统框架二、IoC控制反转 与 DI依赖注入 简单入门三、Bean3.1 Bean的配置3.2 实例化Bean的四种方式3.3 Bean的生命周期 四、依赖注入4.1 setter注入4.2 构造器注入4.3 注入方式选择4.4 依赖自动装配4.5 集合注入4.6 案例&#xff1a;配置数据库4.…

「编程学习书籍总结」提升个人能力从读书开始

✍️作者简介&#xff1a;码农小北&#xff08;专注于Java、Android、Web、TCP/IP等技术方向&#xff09; &#x1f433;博客主页&#xff1a; 开源中国、稀土掘金、51cto博客、博客园、知乎、简书、慕课网、CSDN &#x1f514;如果文章对您有一定的帮助请&#x1f449;关注✨、…

CHINTERGEO2023中国测绘地理信息技术装备展览会,大势智慧在3010展台期待您的莅临!

11月27日-11月29日 CHINTERGEO2023中国测绘地理信息技术装备展览会 二层-HALL3展厅-3010 大势智慧携符合信创要求的实景三维软硬件全流程解决方案 为您带来一场全国产、真安全的实景三维新型智能测绘装备盛宴 期待您的莅临&#xff01;

C#使用MaxMind.GeoIP2数据库查询当前ip地址

GeoLite2-City.mmdb下载 因为比较简单&#xff0c;直接上代码&#xff0c;代码展示获取ip地址的国家和城市信息 using MaxMind.GeoIP2; using MaxMind.GeoIP2.Model; using System; using System.Collections; using System.Collections.Generic; using System.Linq; using Sy…

超级会员卡积分收银系统源码+会员卡+积分商城+多门店系统 附带完整的搭建教程

时代在发展&#xff0c;商家对于会员管理、积分管理、收银管理以及多门店管理的需求日益增长。为了满足这些需求&#xff0c;开发一款功能全面、易于使用和管理的超级会员卡积分收银系统变得至关重要。 以下是部分代码示例&#xff1a; 系统特色功能一览&#xff1a; 1.会员管…

STM32 -Bin/Hex文件格式解析

文章目录 1. 概述2. Hex文件2.1 格式解析2.2 数据类型2.3 举例解析2.4 合并两个Hex文件方法 3. Bin文件3.1 生成方式3.2 合并多个Bin文件方法3.3 打开Bin文件方式3.4 和Hex文件比较 4 总结 1. 概述 Hex文件&#xff1a;它是单片机和嵌入式工程编译输出的一种常见的目标文件格式…

https想访问本地部署的http://localhost接口

情况说明&#xff1a; 网址是https的&#xff0c;想访问java本地启的一个程序接口http://localhost:8089 解决办法 java程序加上

智能高效的转运机器人,为物流行业注入新动力

在当今社会&#xff0c;随着科技的不断发展&#xff0c;机器人已经逐渐融入到我们的生活中。其中&#xff0c;转运机器人作为物流行业的新秀&#xff0c;正以其高效、智能的特点&#xff0c;引起了广泛的关注。 转运机器人&#xff0c;是指能够自主进行物品搬运和运输的机器人…

如何利用CHATGPT写主题文章

问CHAT&#xff1a;新课标下畅言智慧课堂助力小学生量感培养&#xff0c;拟解决的关键问题 CHAT回复&#xff1a; 1. 确定智慧课堂在新课标下的正确应用方法&#xff1a;新课标对教育方法、内容等提出了新的要求&#xff0c;需要探讨如何将智慧课堂与新课标相结合&#xff0c;…

python-opencv 培训课程笔记(2)

python-opencv 培训课程笔记&#xff08;2&#xff09; 1.图像格式转换 先看一下cvtColor函数的例子 #默认加载彩图 pathrD:\learn\photo\cv\cat.jpg# imread(path,way) #way0 灰度图。way1 彩图 #默认彩图 imgcv2.imread(path) img_dogcv2.imread(path_dog) #图片格式的转化…

django restful framework序列化与反序列化

在前后端分离开发中&#xff0c;对于RESTfulAPI设置&#xff0c;一般需要将查询/更新数据以JSON方式进行返回。 序列化 Model.py from django.db import models class User(models.Model):username models.CharField(verbose_name用户名,max_length10)age models.IntegerF…

Cmake 生成器表达式

CMake生成器表达式 - 招财猫的博客 【精选】CMake I 生成器表达式_cmake生成器表达式-CSDN博客 例子 下面这行 就能把Lib的PDB FILEs安装提取出来安装到DESTINATION

VCP-DCV VMware vSphere,即将开课~想了解点击查看

VCP-DCV VMware vSphere 本周开课~ 想报名的必须提前预约啦 &#x1f447;&#x1f447;&#x1f447; 课程介绍 本课程重点讲授如何安装、配置和管理VMware vSphere 8.0&#xff08;包括VMware ESXi™ 8.0和VMware vCenter Server™ 8.0&#xff09; 本课程将帮助您做好…

火电安全事故vr模拟仿真培训强交互更真实

VR消防&#xff0c;利用VR虚拟现实技术&#xff0c;将VR和消防教育融合在一起达到寓教于乐的效果&#xff0c; VR消防教育是对于家中、校园内、大型商场、公司办公室等情景产品研发的消防安全培训类VR系统软件&#xff0c;根据互动体验、互动、视角实际操作、视听觉系统多度自然…