【ProtoBuf】以 “数据秘语” 筑联络:通讯录项目实战 1.0 启步札记

文章目录

  • 引言
  • 筑路之备:快速上手ProtoBuf
  • 步骤一:创建.proto文件
    • ==⽂件规范==
    • ==添加注释==
    • 指定 proto3 语法
    • package 声明符
    • 定义消息(message)
    • 定义消息字段
    • 【定义联系人 message】
    • 字段唯一编号的范围
  • 步骤2:编译 contacts.proto ⽂件,⽣成 C++ ⽂件
  • 编译命令
    • 编译命令⾏格式为:
    • 编译 `contacts.proto` ⽂件命令如下 :
    • 【-I 指定搜索目录】
  • 编译 contact.proto 文件效果
    • 【自动生成方法】:
    • 【序列化和反序列化方法】:
  • 步骤3:序列化与反序列化的使⽤
    • 编译指令:
  • 小结

在这里插入图片描述

引言

为何用 “数据秘语” 编织通讯录?

  • 传统通讯录数据的 “困境”:文本格式(如 JSON/CSV)的冗余与解析慢,恰似 “联络信笺” 的冗长絮语
  • ProtoBuf 的 “秘语优势”:二进制压缩、跨语言兼容,为通讯录搭建 “高效联络通道”
  • 本实战 1.0 的核心目标:用 ProtoBuf 实现 “联系人数据的序列化存储与反序列化读取”,完成基础联络功能

ProtoBuf的后续学习,将通过项目实战——书写通讯录的方式进行

筑路之备:快速上手ProtoBuf

需求:在快速上⼿中,会编写第⼀版本的通讯录 1.0。在通讯录 1.0 版本中,将实现:

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

通过通讯录 1.0,我们便能了解使⽤ProtoBuf初步要掌握的内容,以及体验到 ProtoBuf 的完整使⽤流程

步骤一:创建.proto文件

⽂件规范

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

添加注释

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

指定 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++,命令空间,避免多个message冲突。在通讯录 1.0 的 contacts.proto ⽂件中,可以声明其命名空间,内容如下:

syntax = "proto3";
package contacts;

定义消息(message)

消息(message): 要定义的结构化对象,我们可以给这个结构化对象中定义其对应的属性内容。

【问题】:为什么要定义消息?

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

  • 所以 ProtoBuf 就是以 message 的⽅式来⽀持我们定制协议字段,后期帮助我们形成类和⽅法来使⽤。在通讯录 1.0 中我们就需要为 联系⼈ 定义⼀个 message。
    在这里插入图片描述
    proto ⽂件中定义⼀个消息类型的格式为 :

message 消息类型名{

}

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

定义消息字段

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

  • 字段名称命名规范:全⼩写字⺟,多个字⺟之间⽤ _ 连接
  • 字段类型分为:标量数据类型 和 特殊类型(包括枚举、其他消息类型等)
  • 字段唯⼀编号:⽤来标识字段,不能使用相同的字段编号

【定义联系人 message】

其中,我们必须要把这个字段编号带上,跟PB特点:高效,序列化,结果更小有关系。跟Protobuf编译原理有关。

message PeopleInfo{	//建议驼峰string name = 1;//姓名	标识字段编号int32 age = 2;	//年龄
}

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

在这里插入图片描述

字段唯一编号的范围

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 ⽂件,⽣成 C++ ⽂件

注意:在使用过程中,如果没有"颜色亮光",需要下载Protobuf插件

在这里插入图片描述

编译命令

编译命令⾏格式为:

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

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

protoc --cpp_out=. contacts.proto

【-I 指定搜索目录】

场景:查找contacts.ptoto(文件名)

在这里插入图片描述

编译 contact.proto 文件效果

编译 contacts.proto ⽂件后,会⽣成所选择语⾔的代码,我们选择的是C++,所以编译后⽣成了两个⽂件:
contacts.pb.h contacts.pb.cc

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

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

【自动生成方法】:

在这里插入图片描述
上述的例⼦中:

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

【序列化和反序列化方法】:

序列化和反序列化方法,不在Message类中,在消息类的父类MessageLite 中,提供了读写消息实例的方法,包括了序列化方法和反序列化方法。Message类继承MessageLite父类的属性和方法

在这里插入图片描述
注意:

  • 序列化的结果为⼆进制字节序列,⽽⾮⽂本格式。
  • 以上三种序列化的⽅法没有本质上的区别,只是序列化后输出的格式不同,可以供不同的应⽤场景使⽤。
  • 序列化的 API 函数均为const成员函数,因为序列化不会改变类对象的内容, ⽽是将序列化的结果保存到函数⼊参指定的地址中。
  • 详细 message API 可以参⻅完整列表

步骤3:序列化与反序列化的使⽤

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

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

main.cc

#include<iostream>
#include"contacts.pb.h"int main()
{std::string people_str;{// 对一个联系人使用 PB 进行序列化,并将结果打印出来contacts::PeopleInfo people; // 定义对象people.set_name("张三"); // 设置姓名people.set_age(20); // 设置年龄// 使用正确的序化方法if(!people.SerializeToString(&people_str)){std::cerr << "Failed to serialize people." << std::endl;return -1;}std::cout << "Serialized data: " << people_str << std::endl;}{// 对一个联系人使用 PB 进行反序列化,并将结果打印出来contacts::PeopleInfo people; // 定义对象// 使用正确的反序列化方法if(!people.ParseFromString(people_str)){std::cerr << "Failed to parse people." << std::endl;return -1;}std::cout << "Name: " << people.name() << std::endl; // 获取姓名std::cout << "Age: " << people.age() << std::endl; // 获取年龄}return 0;}

编译指令:

g++ main.cc contacts.pb.cc -o TestPb -std=c++11 -lprotobuf
  • -lprotobuf:必加,不然会有链接错误。
  • -std=c++11:必加,使⽤C++11语法。

执⾏ TestPb,可以看⻅ people 经过序列化和反序列化后的结果:

在这里插入图片描述
由于ProtoBuf是把联系⼈对象序列化成了⼆进制序列,这⾥⽤ string 来作为接收⼆进制序列的容器。所以在终端打印的时候会有换⾏等⼀些乱码显⽰。

所以相对于 xml JSON 来说,因为被编码成⼆进制,破解成本增⼤,ProtoBuf 编码是相对安全的。

在这里插入图片描述

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

小结

当你用 ProtoBuf 的 “数据秘语” 织就通讯录的存储与读取,这座精简高效的联络小筑,便已在你手中落成。

1.0 的启步札记虽止于此,却为你推开了数据通信的门,更精彩的 “秘语续篇”,正待后续续写。

在这里插入图片描述

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

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

相关文章

在 macOS 下升级 Python 几种常见的方法

在 macOS 下升级 Python 有几种常见的方法&#xff0c;具体取决于你最初是如何安装 Python 的。了解你的安装方式是关键。 首先&#xff0c;你需要知道你当前 Python 版本以及它的安装路径。 检查 Python 版本&#xff1a; python --version # 可能指向 Python 2.x python3 …

Linux 入门到精通,真的不用背命令!零基础小白靠「场景化学习法」,3 个月拿下运维 offer,第二十五天

三、Shell脚本编程 Shell脚本语言的运算 算数运算 shell支持算术运算&#xff0c;但只支持整数&#xff0c;不支持小数 Bash中的算术运算 -- 加法运算 -- - 减法运算 -- * 乘法运算 -- / 除法运算 -- % 取模&#xff0c;即取余数 -- ** 乘方 ​ #乘法符号在有些场景需要转…

SpringAI系列---【多租户记忆和淘汰策略】

1.多租户工作原理 2.引入jdbc的pom spring官网链接&#xff1a;https://docs.spring.io/spring-ai/reference/api/chat-memory.html&#xff0c;推荐使用官网的jdbc。 阿里巴巴ai链接&#xff1a;https://github.com/alibaba/spring-ai-alibaba/tree/main/community/memories j…

Linux gzip 命令详解:从基础到高级用法

Linux gzip 命令详解&#xff1a;从基础到高级用法 在 Linux 系统中&#xff0c;文件压缩与解压缩是日常运维和文件管理的常见操作。gzip&#xff08;GNU Zip&#xff09;作为一款经典的压缩工具&#xff0c;凭借其高效的压缩算法和简洁的使用方式&#xff0c;成为 Linux 用户处…

Redis有什么优点和缺点?

优点&#xff1a;极致性能&#xff1a; 基于内存操作和高效的单线程 I/O 模型&#xff0c;读写速度极快。数据结构丰富&#xff1a; 支持多种数据结构&#xff0c;如 String、Hash、List、Set、ZSet、Stream、Geo 等&#xff0c;编程模型灵活。持久化与高可用&#xff1a; 提供…

NestJS 3 分钟搭好 MySQL + MongoDB,CRUD 复制粘贴直接运行

基于上一篇内容《为什么现代 Node 后端都选 NestJS TypeScript&#xff1f;这组合真香了》&#xff0c;这篇文章继续写数据库的连接。 所以今天把MySQL、MongoDB全接上&#xff0c;做个小实例。朋友们项目里用什么数据库可以视情况而定。 这里的功能分别为&#xff1a; MySQ…

用了企业微信 AI 半年,这 5 个功能让我彻底告别重复劳动

每天上班不是在整理会议纪要&#xff0c;就是在翻聊天记录找文件&#xff0c;写文档还要自己抠数据…… 这些重复劳动是不是也在消耗你的时间&#xff1f;作为用了企业微信 AI 功能半年的 “老用户”&#xff0c;我必须说&#xff1a;企业微信 AI 的这 5 个功能&#xff0c;真的…

从入门到高手,Linux就应该这样学【好书推荐】

从入门到高手&#xff0c;请这样学Linux 一、Linux基础与终端操作 1.1 Linux简介 Linux 是一种开源的类 Unix 操作系统&#xff0c;以其稳定性、安全性和高效性被广泛应用于服务器、嵌入式系统及开发环境中。掌握基本命令和操作技巧是 Linux 学习的关键。 1.2 终端基础 打开…

【数据可视化-104】安徽省2025年上半年GDP数据可视化分析:用Python和Pyecharts打造炫酷大屏

&#x1f9d1; 博主简介&#xff1a;曾任某智慧城市类企业算法总监&#xff0c;目前在美国市场的物流公司从事高级算法工程师一职&#xff0c;深耕人工智能领域&#xff0c;精通python数据挖掘、可视化、机器学习等&#xff0c;发表过AI相关的专利并多次在AI类比赛中获奖。CSDN…

组件库UI自动化

一、背景 背景&#xff1a; 组件库全局改动场景多&#xff0c;组件之间耦合场景多–时常需要全场景回归组件库demo有200多个&#xff0c;手动全局回归耗时耗力细微偏差纯视觉无法辨别 可行性分析&#xff1a; 组件库功能占比 L1&#xff08;视觉层&#xff09;&#xff1a;图片…

面试题:JVM与G1要点总结

一.Java内存区域 1.运行时数据区的介绍 2.站在线程的角度看Java内存区域 3.深入分析堆和栈的区别 4.方法的出入栈和栈上分配、逃逸分析及TLAB 5.虚拟机中的对象创建步骤 6.对象的内存布局 1.运行时数据区的介绍 运行时数据区的类型&#xff1a;程序计数器、Java虚拟机栈、本地方…

车辆安全供电系统开发原则和实践

摘要在汽车行业中&#xff0c;安全应用的重要性在不断提升&#xff0c;例如受车辆自动化发展以及机械备用系统重要性降低的影响。为应对这些趋势&#xff0c;安全相关的电气和 / 或电子系统&#xff08;E/E 系统&#xff09;的电源输入必须由供电系统来保障&#xff0c;这使得功…

WebSocket客户端库:websocket-fruge365

&#x1f680; 从零开始打造一个WebSocket客户端库&#xff1a;websocket-fruge365 &#x1f4d6; 前言 在现代Web开发中&#xff0c;实时通信已经成为不可或缺的功能。无论是聊天应用、实时数据监控&#xff0c;还是在线协作工具&#xff0c;WebSocket都扮演着重要角色。然而…

rocketmq批量执行跑批任务报错

rocketmq批量执行跑批任务&#xff0c;报下面的错误&#xff0c;怎么处理一下呢&#xff1f;是修改配置还是修改代码还是&#xff1f; org.apache.rocketmq.client.exception.MQBrokerException: CODE: 215 DESC: [FLOW]client has exhausted the send quota for the current …

大语言模型(LLM)简介与应用分享

1. 什么是大语言模型&#xff08;LLM&#xff09; 大语言模型&#xff08;Large Language Model&#xff0c;简称 LLM&#xff09;是基于 深度学习 和 海量文本数据 训练而成的人工智能模型。 采用 Transformer 架构参数规模巨大&#xff08;数十亿到数千亿&#xff09;能够 理…

【算法笔记】选择排序、插入排序、冒泡排序、二分查找问题

算法的笔记&#xff0c;直接上代码&#xff0c;思路和问题这些&#xff0c;都在代码注释上面 1、工具类 为了生成测试代码和比较器&#xff0c;专门写了一个数组工具类&#xff0c;代码如下&#xff1a; /*** 数组工具类*/ public class ArrUtil {/*** 生成随机数组* 长度是[0,…

行业分享丨基于SimSolid的大型汽车连续冲压模具刚度分析

*本文投稿自机械零部件制造业用户 汽车连续模具的刚度直接决定了冲压件质量&#xff08;尺寸精度、表面缺陷&#xff09;与模具寿命。传统有限元分析&#xff08;FEA&#xff09;在面对大型复杂模具装配体时&#xff0c;存在网格划分困难、计算资源消耗大、周期长等瓶颈。本文以…

用AI生成的html页面设计放到到Axure上实现再改造的方法

要将 AI 生成的 HTML 原型导入 Axure&#xff0c;该方法的核心逻辑是以 Figma 为 “中间桥梁”&#xff08;因 Axure 无法直接读取 HTML&#xff0c;需通过 Figma 转换格式&#xff09;&#xff0c;分 3 步即可完成&#xff0c;以下是详细操作指南&#xff08;含每步目标、具体…

从入门到实战:Linux sed命令全攻略,文本处理效率翻倍

从入门到实战&#xff1a;Linux sed命令全攻略&#xff0c;文本处理效率翻倍 文章目录从入门到实战&#xff1a;Linux sed命令全攻略&#xff0c;文本处理效率翻倍一、认识sed&#xff1a;什么是流编辑器&#xff1f;二、吃透sed工作原理&#xff1a;为什么它能高效处理文本&am…

TIOBE 8月编程语言榜深度解析:Python占比突破26%,Perl成最大黑马

根据TIOBE最新发布的2025年8月编程语言排行榜&#xff0c;一场静默的技术变革正在上演&#xff1a;Python以26.14%的占比首次突破26%大关&#xff0c;连续12个月稳居榜首。这一数据不仅刷新了Python自身的历史纪录&#xff0c;更成为TIOBE指数自2001年创立以来的最高单语言占比…