【快速上手ProtoBuf】基本使用

文章目录

  • 1 :peach:初识 ProtoBuf:peach:
    • 1.1 :apple:序列化概念:apple:
    • 1.2 :apple:ProtoBuf 是什么:apple:
    • 1.3 :apple:ProtoBuf 的使用特点:apple:
  • 2 :peach:创建 .proto ⽂件:peach:
  • 3 :peach:编译 .proto 文件:peach:
  • 3 :peach:序列化与反序列化的使用:peach:


1 🍑初识 ProtoBuf🍑

1.1 🍎序列化概念🍎

序列化和反序列化

  • 序列化:把对象转换为字节序列的过程称为对象的序列化。
  • 反序列化:把字节序列恢复为对象的过程称为对象的反序列化。

什么情况下需要序列化

  • 存储数据:当你想把的内存中的对象状态保存到⼀个⽂件中或者存到数据库中进行持久化时。
  • ⽹络传输:⽹络直接传输数据,但是⽆法直接传输对象,所以要在传输前序列化,传输完成后反序列化成对象。例如我们之前学习过 socket 编程中发送与接收数据。

如何实现序列化

常见的有xmlymljsonprotobuf

1.2 🍎ProtoBuf 是什么🍎

我们先来看看官⽅给出的答案是什么?

  • Protocol Buffers 是 Google 的⼀种语⾔⽆关、平台⽆关、可扩展的序列化结构数据的⽅法,它可⽤于(数据)通信协议、数据存储等。
  • Protocol Buffers 类⽐于 XML,是⼀种灵活,⾼效,⾃动化机制的结构数据序列化⽅法,但是⽐XML 更⼩、更快、更为简单。
  • 你可以定义数据的结构,然后使⽤特殊⽣成的源代码轻松的在各种数据流中使⽤各种语⾔进⾏编写和读取结构数据。你甚⾄可以更新数据结构,⽽不破坏由旧数据结构编译的已部署程序。

简单来讲, ProtoBuf(全称为 Protocol Buffer)是让结构数据序列化的⽅法,其具有以下特点:

  • 语⾔⽆关、平台⽆关:即 ProtoBuf ⽀持 Java、C++、Python 等多种语⾔,⽀持多个平台。
  • ⾼效:即⽐ XML 更⼩、更快、更为简单。
  • 扩展性、兼容性好:你可以更新数据结构,⽽不影响和破坏原有的旧程序。

1.3 🍎ProtoBuf 的使用特点🍎

在这里插入图片描述

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

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


2 🍑创建 .proto ⽂件🍑

⽂件规范

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

指定 proto3 语法

Protocol Buffers 语⾔版本3,简称 proto3,是 .proto ⽂件最新的语法版本。proto3 简化了 ProtocolBuffers 语⾔,既易于使⽤,⼜可以在更⼴泛的编程语⾔中使⽤。它允许你使⽤ Java,C++,Python等多种语⾔⽣成 protocol buffer 代码。

.proto ⽂件中,要使⽤ syntax = "proto3"; 来指定⽂件语法为 proto3,并且必须写在除去注释内容的第⼀⾏。 如果没有指定,编译器会使⽤proto2语法。

package 声明符

package 是⼀个可选的声明符,能表⽰ .proto ⽂件的命名空间,在项⽬中要有唯⼀性。它的作⽤是为了避免我们定义的消息出现冲突。(类似于C++中的namespace)
比如我们就可以像这么写:

syntax = "proto3";
package contacts;

定义消息(message)

消息(message): 要定义的结构化对象,我们可以给这个结构化对象中定义其对应的属性内容。
这⾥再提⼀下为什么要定义消息?在⽹络传输中,我们需要为传输双⽅定制协议。定制协议说⽩了就是定义结构体或者结构化数据,⽐如,tcp,udp 报⽂就是结构化的。再⽐如将数据持久化存储到数据库时,会将⼀系列元数据统⼀⽤对象组织起来,再进⾏存储。

消息类型命名规范:使⽤驼峰命名法,⾸字⺟⼤写。

定义消息字段

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

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

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

.proto TypeNotesC++ Type
doubledouble
floatfloat
int32使⽤变⻓编码[1]。负数的编码效率较低⸺若字段可能为负值,应使⽤ sint32 代替int32
int64使⽤变⻓编码[1]。负数的编码效率较低⸺若字段可能为负值,应使⽤ sint34 代替int64
uint32使⽤变⻓编码[1]。uint32
uint64使⽤变⻓编码[1]。uint64
sint32使⽤变⻓编码[1]。符号整型。负值的编码效率⾼于常规的 int32 类型int32
sin64使⽤变⻓编码[1]。符号整型。负值的编码效率⾼于常规的 int64 类型int64
fixed32定⻓ 4 字节。若值常⼤于228 则会⽐ uint32 更⾼效uint32
fixed64定⻓ 8 字节。若值常⼤于228 则会⽐ uint32 更⾼效uint64
sfixed32定⻓ 4 字节int32
sfixed64定⻓ 8 字节int64
boolbool
string包含 UTF-8 和 ASCII 编码的字符串,⻓度不能超过232string
bytes可包含任意的字节序列但⻓度不能超过 232string

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

此时我们就可以这样写:

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

在这⾥还要特别讲解⼀下字段唯⼀编号

1 ~ 536,870,911 (229 - 1) ,其中 19000 ~ 19999 不可⽤。

9000 ~ 19999 不可⽤是因为:在 Protobuf 协议的实现中,对这些数进⾏了预留。如果⾮要在.proto⽂件中使⽤这些预留标识号,例如将 name 字段的编号设置为19000,编译时就会报警。

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


3 🍑编译 .proto 文件🍑

编译命令⾏格式为:

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

当我们编译成功后就会生成两个文件,一个头文件,一个源文件:
在这里插入图片描述

对于编译⽣成的 C++ 代码:

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

我们在VSCode下观察 .h文件:在这里插入图片描述提示:有时候在查看时会出现大量飘红现象,这是由于插件的原因,本身是没有错误的。

  • 每个字段都有设置和获取的⽅法, get 的名称与⼩写字段完全相同,set ⽅法以 set_ 开头。
  • 每个字段都有⼀个 clear_ ⽅法,可以将字段重新设置回 empty 状态。

除此之外包括序列化⽅法和反序列化⽅法,这里列举小部分供参考:

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

查看更加详细的API点击这里:【API】


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

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

  • 对⼀个联系⼈的信息使⽤ PB 进⾏序列化,并将结果打印出来。
  • 对序列化后的内容使⽤ PB 进⾏反序列,解析出联系⼈信息并打印出来。

参考代码:

#include <iostream>
#include "contacts.pb.h" // 引⼊编译⽣成的头⽂件
using namespace std;int main()
{string people_str;{contacts::PeopleInfo people;people.set_age(21);people.set_name("虾头男");// 调⽤序列化⽅法,将序列化后的⼆进制序列存⼊string中if (!people.SerializeToString(&people_str)){cout << "序列化联系⼈失败." << endl;}// 打印序列化结果cout << "序列化后的 people_str: " << people_str << endl;}{contacts::PeopleInfo people;// 调⽤反序列化⽅法,读取string中存放的⼆进制序列,并反序列化出对象if (!people.ParseFromString(people_str)){cout << "反序列化出联系⼈失败." << endl;}// 打印结果cout << "Parse age: " << people.age() << endl;cout << "Parse name: " << people.name() << endl;}
}

makefile:

test:main.ccg++ -o $@ $^ contacts.pb.cc -std=c++11 -lprotobuf
.PHONY:clean
clean:rm -r test

执行:
在这里插入图片描述
我们发现报了一个错误,原因是系统找不到共享库,我们执行下面命令即可:

sudo vim /etc/ld.so.conf
#添加以下路径
/usr/local/lib

修改/etc/ld.so.conf需要root权限。
然后执行:

sudo ldconfig

我们重新编译生成:
在这里插入图片描述
发现符合预期结果。由于序列化的结果是二进制,所以有些内容没有打印出来乱码很正常。
所以相对于 xmlJSON 来说,因为被编码成⼆进制,破解成本增⼤,ProtoBuf 编码是相对安全的。


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

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

相关文章

LeetCode 刷题 [C++] 第45题.跳跃游戏 II

题目描述 给定一个长度为 n 的 0 索引整数数组 nums。初始位置为 nums[0]。 每个元素 nums[i] 表示从索引 i 向前跳转的最大长度。换句话说&#xff0c;如果你在 nums[i] 处&#xff0c;你可以跳转到任意 nums[i j] 处: 0 < j < nums[i]i j < n 返回到达 nums[n …

【C语言】熟悉文件基础知识

欢迎关注个人主页&#xff1a;逸狼 创造不易&#xff0c;可以点点赞吗~ 如有错误&#xff0c;欢迎指出~ 文件 为了数据持久化保存&#xff0c;使用文件&#xff0c;否则数据存储在内存中&#xff0c;程序退出&#xff0c;内存回收&#xff0c;数据就会丢失。 程序设计中&…

微信小程序,h5端自适应登陆方式

微信小程序端只显示登陆(获取opid),h5端显示通过账户密码登陆 例如: 通过下面的变量控制: const isWeixin ref(false); // #ifdef MP-WEIXIN isWeixin.value true; // #endif

LIN基础:从LIN Frame开始

目录&#xff1a; 1、LIN的网络拓扑 2、LIN Frame 1&#xff09;Header 2&#xff09;Response 3、LIN的通信规则 1&#xff09;LIN的发送行为示例 2&#xff09;LIN的接收行为示例 虽然LIN总线的通信速率不高&#xff0c;工程中&#xff0c;最高的速率也就19200bps。…

StarRocks——Stream Load 事务接口实现原理

目录 前言 一、StarRocks 数据导入 二、StarRocks 事务写入原理 三、InLong 实时写入StarRocks原理 3.1 InLong概述 3.2 基本原理 3.3 详细流程 3.3.1 任务写入数据 3.3.2 任务保存检查点 3.3.3 任务如何确认保存点成功 3.3.4 任务如何初始化 3.4 Exactly Once 保证…

Leetcode - 周赛386

目录 一&#xff0c;3046. 分割数组 二&#xff0c;3047. 求交集区域内的最大正方形面积 三&#xff0c;3048. 标记所有下标的最早秒数 I 四&#xff0c;3049. 标记所有下标的最早秒数 II 一&#xff0c;3046. 分割数组 将题目给的数组nums分成两个数组&#xff0c;且这两个…

盲人出行:科技创造美好的未来

在繁忙的都市中&#xff0c;我每天都要面对许多挑战&#xff0c;盲人出行安全保障一直难以得到落实。我看不见这个世界&#xff0c;只能依靠触觉和听觉来感知周围的一切。然而&#xff0c;我从未放弃过对生活的热爱和对未来的憧憬。在一次机缘巧合下&#xff0c;我认识了一款名…

C3_W2_Collaborative_RecSys_Assignment_吴恩达_中英_Pytorch

Practice lab: Collaborative Filtering Recommender Systems(实践实验室:协同过滤推荐系统) In this exercise, you will implement collaborative filtering to build a recommender system for movies. 在本次实验中&#xff0c;你将实现协同过滤来构建一个电影推荐系统。 …

VLAN实验报告

实验要求&#xff1a; 实验参考图&#xff1a; 实验过程&#xff1a; r1: [r1]int g 0/0/0.1 [r1-GigabitEthernet0/0/0.1]ip address 192.168.1.1 24 [r1-GigabitEthernet0/0/0.1]dot1q termination vid 2 [r1-GigabitEthernet0/0/0.1]arp broadcast enable [r1]int g 0/0/…

Mysql学习之MVCC解决读写问题

多版本并发控制 什么是MVCC MVCC &#xff08;Multiversion Concurrency Control&#xff09;多版本并发控制。顾名思义&#xff0c;MVCC是通过数据行的多个版本管理来实现数据库的并发控制。这项技术使得在InnoDB的事务隔离级别下执行一致性读操作有了保证。换言之&#xff0…

django的模板渲染中的【高级定制】:按数据下标id来提取数据

需求&#xff1a; 1&#xff1a;在一个页面中显示一张数据表的数据 2&#xff1a;不能使用遍历的方式 3&#xff1a;页面中的数据允许通过admin后台来进行修改 4&#xff1a;把一张数据表的某些内容渲染到[xxx.html]页面 5&#xff1a;如公司的新商品页面&#xff0c;已有固定的…

《梦幻西游》本人收集的34个单机版游戏,有详细的视频架设教程,值得收藏

梦幻西游这款游戏&#xff0c;很多人玩&#xff0c;喜欢研究的赶快下载吧。精心收集的34个版本。不容易啊。里面有详细的视频架设教程&#xff0c;可以外网呢。 《梦幻西游》本人收集的34个单机版游戏&#xff0c;有详细的视频架设教程&#xff0c;值得收藏 下载地址&#xff1…

阶跃信号与冲击信号

奇异信号&#xff1a;信号与系统分析中&#xff0c;经常遇到函数本身有不连续点&#xff08;跳变电&#xff09;或其导函数与积分有不连续点的情况&#xff0c;这类函数称为奇异函数或奇异信号&#xff0c;也称之为突变信号。以下为一些常见奇异函数。 奇异信号 单位斜变信号 …

Ubuntu18.04安装RTX2060显卡驱动+CUDA+cuDNN

Ubuntu18.04安装RTX2060显卡驱动CUDAcuDNN 1 安装RTX2060显卡驱动1.1 查看当前显卡是否被识别1.2 安装驱动依赖1.3 安装桌面显示管理器1.4 下载显卡驱动1.5 禁用nouveau1.6 安装驱动1.7 查看驱动安装情况 2 安装CUDA2.1 查看当前显卡支持的CUDA版本2.2 下载CUDA Toolkit2.3 安装…

车灯修复UV胶的优缺点有哪些?

车灯修复UV胶的优点如下&#xff1a; 优点&#xff1a; 快速固化&#xff1a;通过紫外光照射&#xff0c;UV胶可以在5-15秒内迅速固化&#xff0c;提高了修复效率。高度透明&#xff1a;固化后透光率高&#xff0c;几乎与原始车灯材料无法区分&#xff0c;修复后车灯外观更加…

对缓冲区的初步认识——制作进度条小程序

对缓冲区的初步认识--进度条小程序 前言预备知识回车和换行的区别输出缓冲区/n 有清空输出缓冲区的作用stdout是什么&#xff1f;验证一切皆文件为什么是\n行刷新&#xff1f; 倒计时程序原理 代码实现为什么这里要强制刷新&#xff1f;没有会怎样&#xff1f;为什么是输出的是…

RabbitMQ安装及使用

系列文章目录 文章目录 系列文章目录前言一、下载二、安装三、插件安装四、配置五、权限六、集群模式 前言 前些天发现了一个巨牛的人工智能学习网站&#xff0c;通俗易懂&#xff0c;风趣幽默&#xff0c;忍不住分享一下给大家。点击跳转到网站&#xff0c;这篇文章男女通用&…

【MATLAB源码-第154期】基于matlab的OFDM系统多径信道下块状和梳妆两种导频插入方式误码率对比仿真。

操作环境&#xff1a; MATLAB 2022a 1、算法描述 OFDM&#xff08;Orthogonal Frequency Division Multiplexing&#xff0c;正交频分复用&#xff09;是一种高效的无线信号传输技术&#xff0c;广泛应用于现代通信系统&#xff0c;如Wi-Fi、LTE和5G。OFDM通过将宽带信道划分…

RK3568 Android12 适配抖音 各大APP

RK3568 Android12 适配抖音 各大APP SOC RK3568 system:Android 12 平台要适配抖音和各大APP 平台首先打开抖音发现摄像头预览尺寸不对只存在右上角,我将抖音APP装在手机上预览,发现是全屏 一开始浏览各大博客 给出的解决方法是修改framework 设置为全屏显示: framewo…

Tomcat基础及与Nginx实现动静分离,搭建高效稳定的个人博客系统

目录 引言 一、TOMCAT基础功能 &#xff08;一&#xff09;自动解压war包 &#xff08;二&#xff09;状态页 1.登录状态页 2.远程登录 &#xff08;三&#xff09;服务管理界面 &#xff08;四&#xff09;Host虚拟主机 1.设置虚拟主机 2.建立站点目录与文件 二、实…