【ProtoBuf】文件编写及序列化

ProtoBuf文件编写及序列化

文章目录

  • ProtoBuf文件编写及序列化
  • 快速上手ProtoBuf
    • 创建.proto 文件
      • 指定Proto3语法
      • Package声明符
      • 定义消息(message)
      • 定义消息字段
      • 编译命令
  • 序列化与反序列化的使用
  • 小结


快速上手ProtoBuf

为了快速上手以及完整的使用ProtoBuf,我们将编写一个小项目,并根据PB学习程度对这个项目来逐渐改版,每一个版本对应PB的新知识点。在后续内容中,会使用简单的通讯录作为项目实现。

首先来确定第一版的通讯录要求:

  • 对一个联系人的信息使用PB进行序列化,并将结果打印出来。
  • 对序列化后的内容使用PB进行反序列化,解析出联系人信息并打印出来。
  • 联系人包含以下信息:姓名、年龄。

通过这个通讯录1.0版本,我们能快速了解并使用PB初步要掌握的内容,以及体验到PB的完整使用流程。

创建.proto 文件

文件规范

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

我们为通讯录1.0新建文件:contacts.proto

注释

注释类型与C/C++中注释相同,使用 “//” 或者 “/* xxx*/”

指定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";

Package声明符

package是一个可选的声明符,能表示.proto文件的命名空间(相当于C++中的namesapce),在项目中要有唯一性。

在通讯录1.0的contacts.proto文件中,我们可以声明命名空间如下:

syntax = "proto3";
package contacts;

定义消息(message)

消息(message): 要定义的结构化对象,我们可以给这个结构化对象中定义其对应的属性内容。这⾥再提⼀下为什么要定义消息?

在网络传输中,我们需要为传输双⽅定制协议。定制协议说白了就是定义结构体或者结构化数据,
比如,tcp,udp 报文就是结构化的。再⽐如将数据持久化存储到数据库时,会将⼀系列元数据统一用对象组织起来,再进行存储

所以 ProtoBuf 就是以 message 的⽅式来⽀持我们定制协议字段,后期帮助我们形成类和⽅法来使⽤。在通讯录 1.0 中我们就需要为 联系⼈ 定义⼀个 message。

在.proto文件中定义一个消息类型的格式为:

message 消息类型名 // 建议使用驼峰命名法
{}

为contacts.proto新增联系人message内容如下:

syntax = "proto3"; // 需要指定为proto3,否则默认为proto2语法
package contacts; // package是命名空间// 定义联系人message
message PeopleInfo
{}

定义消息字段

在message中我们可以定义其属性字段,字段定义格式为:字段类型 字段名 = 字段唯一编号;

  • 字段名称命名规范:全小写字母,多个字母之间用_ 连接。
  • 字段类型分为:标量数据类型 和 特殊类型(包括枚举、其他消息类型等)。
  • 字段唯⼀编号:⽤来标识字段,⼀旦开始使⽤就不能够再改变。

以下表格展⽰了定义于消息体中的标量数据类型,以及编译 .proto ⽂件之后⾃动⽣成的类中与之对应的
字段类型。在这⾥展⽰了与 C++ 语⾔对应的类型:

.proto TypeNotesC++ Type
doubledouble
floatfloat
int32使⽤变⻓编码[1]。负数的编码效率较低⸺若字段可能为负值,应使⽤ sint32 代替。int32
int64使⽤变⻓编码[1]。负数的编码效率较低⸺若字段可能为负值,应使⽤ sint64 代替。int64
uint32使⽤变⻓编码[1]uint32
uint64使⽤变⻓编码[1]uint64
sint32使⽤变⻓编码[1]。符号整型。负值的编码效率⾼于常规的 int32 类型int32
sint64使⽤变⻓编码[1]。符号整型。负值的编码效率⾼于常规的 int64 类型int64
fixed32定⻓ 4 字节。若值常⼤于2^28 则会⽐ uint32 更⾼效uint32
fixed64定⻓ 8 字节。若值常⼤于2^56 则会⽐ uint64 更⾼效uint64
sfixed32定长4字节int32
sfixed64定长8字节int64
boolbool
string包含 UTF-8 和 ASCII 编码的字符串,⻓度不能超过2^32string
bytes可能包含任意的字节序列但长度不能超过2^32string
  • 注意:【1】变长编码是指:经过protobuf编码后,原本4字节或8字节的数可能会被变为其他字节数。

更新contacts.proto, 新增姓名、年龄字段:

// 首行:语法指定行(去除注释外的第一行)
/*首行:语法指定行(去除注释外的第一行)*/
syntax = "proto3"; // 需要指定为proto3,否则默认为proto2语法
package contacts; // package是命名空间// 定义联系人message
// 结构中,给每个成员都赋值,这是语法必须规定,作为字段编号, 用来标识字段
message PeopleInfo
{string name = 1; // 姓名int32 age = 2;   // 年龄
}

字段唯一编号是有一定范围的:

1 ~ 536,870,911(2^29 - 1),其中19000~19999不可用

19000~19999不可用是因为:在PB协议实现中,对这些数进行了预留。如果非要在.proto文件中使用这些预留标识号,例如将name字段的编号设置为19000,编译时机会报警。

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

编译命令

编译命令的格式为:

protoc [--proto_path=IMPORT_PATH] --cpp_out=DST_DIR path/to/file.protoprotoc 是 Protocol Buffer 提供的命令⾏编译⼯具。--proto_path 指定 被编译的.proto⽂件所在⽬录,可多次指定。可简写成 -I IMPORT_PATH 。如不指定该参数,则在当前⽬录进⾏搜索。
当某个.proto ⽂件 import 其他.proto ⽂件时, 或需要编译的 .proto ⽂件不在当前⽬录下,这时就要⽤-I来指定搜索⽬录。--cpp_out= 指编译后的⽂件为 C++ ⽂件。
OUT_DIR 编译后⽣成⽂件的⽬标路径。
path/to/file.proto 要编译的.proto⽂件。

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

protoc --cpp_out=. contacts.proto

我们在终端使用这个命令后:

在这里插入图片描述

我们发现在同级目录中自动生成了contacts.pb.h文件和contacts.pb.cc文件,分别用来存放类的声明和类的实现。

Contacts.pb.h部分demo展示:

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成员函数,因为序列化不会改变类对象的内容, 而是将序列化的结果保存到函数入参指定的地址中。

序列化与反序列化的使用

创建⼀个测试⽂件 main.cc,⽅法中我们实现:

  • 对⼀个联系⼈的信息使⽤ PB 进⾏序列化,并将结果打印出来。
  • 对序列化后的内容使⽤ PB 进⾏反序列,解析出联系⼈信息并打印出来。
#include <iostream>
#include <string>
#include "contacts.pb.h"int main()
{std::string msg;// 对一个联系人的信息使用 PB 进行序列化,并将结果答应出来contacts::PeopleInfo people;people.set_name("张三");people.set_age(20);if(!people.SerializeToString(&msg)){std::cerr << "序列化联系人失败!" << std::endl;return -1;}std::cout << "序列化成功,结果: " << msg << std::endl;// 对序列化后的内容使用 PB 进行反序列化,解析出联系人信息并打印出来contacts::PeopleInfo people_result;if(!people_result.ParseFromString(msg)){std::cerr << "反序列化联系人失败!" << std::endl;return -1;}std::cout << "反序列化成功! " << std::endl<< "姓名: " << people_result.name() << std::endl<< "年龄: " << people_result.age() << std::endl;return 0;
}

然后我们进行编译,因为使用了protobuf,而protobuf实际上使用了C++11的新特性:

  • -std=c++11: 必加,使用C++11语法。
  • -lprotobuf:必加,链接protubuf库。

在这里插入图片描述
在这里插入图片描述
由于 ProtoBuf 是把联系⼈对象序列化成了⼆进制序列,这⾥⽤ string 来作为接收⼆进制序列的容器。所以在终端打印的时候会有换⾏等⼀些乱码显⽰。所以相对于 xml 和 JSON 来说,因为被编码成⼆进制,破解成本增⼤,ProtoBuf 编码是相对安全的。

小结

在这里插入图片描述

  1. 编写.proto 文件,目的是为了定义结构对象(message)及属性内容。
  2. 使用 protoc 编译器编译 .proto ⽂件,生成⼀系列接口代码,存放在新生成头文件和源⽂件中。
  3. 依赖生成的接口,将编译生成的头⽂件包含进我们的代码中,实现对 .proto 文件中定义的字段进行设置和获取,和对 message 对象进⾏序列化和反序列化。

总的来说ProtoBuf 是需要依赖通过编译⽣成的头文件和源文件来使用的。


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

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

相关文章

Java高频面试之SE-22

hello啊&#xff0c;各位观众姥爷们&#xff01;&#xff01;&#xff01;本baby今天又来了&#xff01;哈哈哈哈哈嗝&#x1f436; Java中的Optional了解多少&#xff1f; 在 Java 中&#xff0c;Optional 是 Java 8 引入的一个容器类&#xff0c;用于显式处理可能为 null 的…

250217-数据结构

1. 定义 数据结构是数据的存储结构&#xff0c;即数据是按某些结构来存储的&#xff0c;比如线性结构&#xff0c;比如树状结构等。 2. 学习意义 数据结构是服务于算法的&#xff0c;为了实现算法的高效计算&#xff0c;所以将数据按特定结构存储。比如使用快速插入或删除的…

PyCharm2024使用Python3.12在Debug时,F8步进时如同死机状态

在使用时PyCharm2024&#xff0b;Python3.12&#xff0c;在程序进行调试时&#xff0c;按F8步进时如同死机状态。 1、相同的程序在PyCharm2023&#xff0b;Python3.9时是没有问题的&#xff0c;因此决定重装PyCharm2023&#xff0b;Python3.9&#xff0c;进行调试——调试OK。 …

C/C++ | 每日一练 (2)

&#x1f4a2;欢迎来到张胤尘的技术站 &#x1f4a5;技术如江河&#xff0c;汇聚众志成。代码似星辰&#xff0c;照亮行征程。开源精神长&#xff0c;传承永不忘。携手共前行&#xff0c;未来更辉煌&#x1f4a5; 文章目录 C/C | 每日一练 (2)题目参考答案封装继承多态虚函数底…

DeepSeek应用-一秒对书本要点分析并创建思维脑图

2025年开始啦&#xff0c;从DeepSeek的火爆程度来看&#xff0c;今年必须紧盯DS的发展&#xff0c;AI不会淘汰人&#xff0c;AI只会淘汰不会使用的人。从文心一言、豆包、Kimi到DS,基本上从功能上大致相同&#xff0c;但是DeepSeek的开源着实在眼界和格局上更胜一筹&#xff0c…

4、IP查找工具-Angry IP Scanner

在前序文章中&#xff0c;提到了多种IP查找方法&#xff0c;可能回存在不同场景需要使用不同的查找命令&#xff0c;有些不容易记忆&#xff0c;本文将介绍一个比较优秀的IP查找工具&#xff0c;可以应用在连接树莓派或查找IP的其他场景中。供大家参考。 Angry IP Scanner下载…

android 的抓包工具

charles 抓包工具 官网地址 nullCharles Web Debugging Proxy - Official Sitehttps://www.charlesproxy.com/使用手册一定记得看官网 SSL Certificates • Charles Web Debugging Proxy http请求&#xff1a; 1.启动代理&#xff1a; 2.设置设备端口 3.手机连接当前代理 …

Java常用工具类详解

目录 一、Java 中的数学利器&#xff1a;java.lang.Math 类详解 1.常用属性 2.常用方法 ⑴.static int abs(int a) ⑵.static double ceil(double a) ⑶.static double floor(double a) ⑷.static int max(int a, int b) 和 static int min(int a, int b) ⑸.static do…

STM32 低功耗模式

目录 背景 低功耗模式 睡眠模式 进入睡眠模式 退出睡眠模式 停止模式 进入停止模式 退出停止模式 待机模式 进入待机模式 退出待机模式 程序 睡眠模式 休眠模式配置 进入休眠模式 退出睡眠模式 停止模式 停止模式配置 进入停止模式 退出停止模式 待机模式…

uniapp 使用v-html在微信小程序中渲染成rich-text如何显示文本溢出省略

一、问题描述 小伙伴有个需求&#xff0c;想在uniapp开发的微信小程序的一个列表中对内容进行显示溢出显示省略号的控制&#xff1a;当文本超出一行之后&#xff0c;显示…。 经过尝试&#xff0c;无法在v-html所在的节点进行ellipise的控制。 二、解决方案 1.增加函数&…

VMware 17 安装 VMTools(win 7旗舰 X64)

由于在VM 17中安装的 win 7虚拟机没有安装VM Tools 的原因&#xff0c;界面有大黑边&#xff0c;也无法直接拖拽复制粘贴文件&#xff08;但是如果只是要复制文件&#xff0c;最简单的方法还是使用U盘&#xff09;&#xff0c;所以下面开始安装VM Tools 。 若直接选择VM软件中的…

【MySQL】我在广州学Mysql 系列——Mysql 日志管理详解

ℹ️大家好&#xff0c;我是练小杰&#xff0c;今天又是新的一周了&#xff0c;又该摆好心态迎接美好的明天了&#xff01;&#xff01;&#xff01;&#x1f606; 本文主要对Mysql数据库中的日志种类以及基本命令进行讨论&#xff01;&#xff01; 回顾&#xff1a;&#x1f4…

python学opencv|读取图像(六十五)使用cv2.boundingRect()函数实现图像轮廓矩形标注

【1】引言 前序学习进程中&#xff0c;已经使用cv2.findContours()函数cv2.drawContours()函数实现图像轮廓识别和标注&#xff0c;这种标注沿着图像的轮廓进行&#xff0c;比较细致。相关文章链接为&#xff1a; python学opencv|读取图像&#xff08;六十四&#xff09;使用…

DeepSeek-V3 技术报告

DeepSeek-V3 Technical Report https://arxiv.org/abs/2412.19437 1. 核心贡献 DeepSeek-V3 是一个拥有 6710 亿参数的大规模混合专家&#xff08;MoE&#xff09;语言模型&#xff0c;每个 token 激活 370 亿参数。 该模型通过创新的架构设计和训练策略&#xff0c;实现了高效…

PCIe7.0信号完整性优化的一些方向

首先考虑过孔stub的影响&#xff0c;分别仿真10mil stub&#xff0c;6mil stub&#xff0c;3mil stub以及无stub四种情况&#xff0c;观察insertion loss/ return loss/TDR Impedance profile、crosstalk四个参数对比情况。 仿真对比结果如下&#xff1a; 其次&#xff0c;考虑…

学习threejs,使用PointLight点光源

&#x1f468;‍⚕️ 主页&#xff1a; gis分享者 &#x1f468;‍⚕️ 感谢各位大佬 点赞&#x1f44d; 收藏⭐ 留言&#x1f4dd; 加关注✅! &#x1f468;‍⚕️ 收录于专栏&#xff1a;threejs gis工程师 文章目录 一、&#x1f340;前言1.1 ☘️THREE.PointLight 二、&…

30填学习自制操作系统第二天

今天要干什么&#xff1f; 初步了解汇编语言使用汇编重新写个昨天的镜像文件继续开发 一: 什么是电信号&#xff1f; 电脑的处理中心是CPU&#xff0c;即“central process unit”的缩写&#xff0c;翻译成中文就是“中央处理单元”&#xff0c;顾名思义&#xff0c;他就是…

Python的顺序结构和循环结构

文章目录 一、条件语句&#xff08;1&#xff09;条件语句的定义&#xff08;2&#xff09;条件语句的语法&#xff08;a&#xff09;单分支 if&#xff08;b&#xff09;双分支 if-else&#xff08;c&#xff09;多分支 if-elif-elif-...-else &#xff08;3&#xff09;注意事…

金蝶云星空点击按钮实现指定文件下载

文章目录 金蝶云星空点击按钮实现指定文件下载业务需求开发实现 金蝶云星空点击按钮实现指定文件下载 业务需求 点击按钮&#xff0c;下载excel 开发实现 创建表单插件&#xff0c;在按钮点击事件&#xff0c;调用附件下载窗口进行指定路径的指定文件下载 模板存放路径 …

EasyExcel的简单使用

EasyExcel使用 官方文档&#xff1a;关于EasyExcel 1.1EasyExcel相关依赖 <dependency><groupId>com.alibaba</groupId><artifactId>easyexcel</artifactId><version>2.2.11</version></dependency> 1.2 写Excel 1.2.1 最…