Protobuf:原理、用法与 C++ 实践

news2024/11/13 18:04:41

在当今的软件开发领域,高效的数据序列化和通信协议是构建高性能、可扩展系统的关键。Protobuf(Protocol Buffers)作为一种流行的开源序列化框架,正因其出色的性能、简洁的语法和跨语言支持而备受青睐。本文将深入探讨 Protobuf 的原理和用法,并通过 C++ 代码进行实际操作演示。

一、Protobuf 原理

Protobuf 是一种轻便高效的结构化数据存储格式,类似于 XML 或 JSON,但具有更小的序列化后数据大小和更高的解析速度。它通过定义一个 .proto 文件来描述数据结构,然后使用 Protobuf 编译器将其生成特定语言(如 C++、Java、Python 等)的代码。

在序列化过程中,Protobuf 根据定义的字段规则,将数据转换为紧凑的二进制格式。在反序列化时,能够准确无误地还原出原始数据结构。

它具有以下特点:

• 语⾔⽆关、平台⽆关:即 ProtoBuf ⽀持 Java、C++、Python 等多种语⾔,⽀持多个平台

• ⾼效:即⽐ XML 更⼩、更快、更为简单

• 扩展性、兼容性好:你可以更新数据结构,⽽不影响和破坏原有的旧程序

使用流程:

• 编写.proto⽂件,⽬的是为了定义结构对象(message)及属性内容

 • 使⽤protoc编译器编译.proto⽂件,⽣成⼀系列接⼝代码,存放在新⽣成头⽂件和源⽂件中

 • 依赖⽣成的接⼝,将编译⽣成的头⽂件包含进我们的代码中,实现对.proto⽂件中定义的字段进⾏ 设置和获取,和对message对象进⾏序列化和反序列化


二、Protobuf 用法

1、定义 .proto 文件
首先,我们需要创建一个 .proto 文件来定义我们的数据结构。以下是一个简单的示例:

//声明proto语法版本
syntax = "proto3";
 //声明代码的命名空间
package contacts;

//定义结构化对象:类型名 字段名 = 字段编号
message PeopleInfo {
 string name = 1; 
 int32 age = 2; 
}

在上述示例中,我们定义了一个名为 Person 的消息类型,包含了 name(字符串)、age(整数)和 is_married(布尔值)三个字段。

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

• 字段名称命名规范:全⼩写字⺟,多个字⺟之间⽤_连接。 

• 字段类型分为:标量数据类型和特殊类型(包括枚举、其他消息类型等)。

 • 字段唯⼀编号:⽤来标识字段,⼀旦开始使⽤就不能够再改变。

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

2、编译 .proto 文件
使用 Protobuf 编译器(如 protoc)将 .proto 文件编译为目标语言的代码。

protoc cpp_out=output_directory student.proto

其中,--cpp_out 指定输出目录为 output_directory

3、在 C++ 中使用 Protobuf

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

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

contacts.pb.h 部分代码展示

class PeopleInfo final : public ::PROTOBUF_NAMESPACE_ID::Message {
 public:
 using ::PROTOBUF_NAMESPACE_ID::Message::CopyFrom;
 1
 2
 3 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 成员函数,因为序列化不会改变类对象的内容,而是将序列化的结果保存到函数入参指定的地址中
• 详细 message API 可以参⻅ 完整列表

序列化与反序列化的使用

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

#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: " << people_str.size() << endl; 
 }

// 反序列化  
 {
 contacts::PeopleInfo people; 
 // 调用反序列化方法,读取 string 中存放的二进制序列,并反序列化出对象 
 if (!people.ParseFromString(people_str)) { 
 cout << "反序列化出联系人失败." << endl; 
 } 
 // 打印结果 
 cout << "Parse age: " << people.age() << endl; 
 cout << "Parse name: " << people.name() << endl; 
 }
 return 0;
} 

• 代码书写完成后,编译 info.cc ,生成可执行程序

1 g++ info.cc contacts.pb.cc -o info -std=c++11 -lprotobuf

◦ -lprotobuf:链接 protobuf 库文件
◦ -std=c++11:支持 C++11

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

序列化后的 people_str: 10
Parse age: 20
Parse name: 张三

由于 ProtoBuf 是把联系人对象序列化成了二进制序列,这里用 string 来作为接收二进制序列的容器。相对于 xml 和 JSON 来说,因为 PB 被编码成二进制,破解成本增大,ProtoBuf 编码是相对安全的。

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

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

相关文章

织梦cms站长导航网站源码

介绍&#xff1a; 源码仅供技术参考严禁商业用途&#xff01;版权归原作者所有&#xff01; 源码上传 到服务器 导航源码安装&#xff1a;域名/install/ 后台信息 后台地址&#xff1a;域名/dede 后台账号&#xff1a;admin 后台密码&#xff1a;7206876 代码下载&#x…

【代码随想录】区间和——前缀和方法

本博文为《代码随想录》学习笔记&#xff0c;原文链接&#xff1a;代码随想录 题目 原题链接&#xff1a;58. 区间和&#xff08;第九期模拟笔试&#xff09; 题目描述 给定一个整数数组 Array&#xff0c;请计算该数组在每个指定区间内元素的总和。 输入描述 第一行输入为…

求职 day13总结

总结一下周一的平安寿险电话面&#xff08;7月29&#xff09;和周二&#xff08;7月30&#xff09;华为od的技术一面、二面、&#xff08;8月1&#xff09;资面和等结果流程。 面试前压力挺大的&#xff0c;毕竟这么久还没有技术面过。周日还想再推一下od技术面时间&#xff0…

【C语言】数据结构前言

&#x1f984;个人主页:小米里的大麦-CSDN博客 &#x1f38f;所属专栏:C语言数据结构_小米里的大麦的博客-CSDN博客 &#x1f381;代码托管:黄灿灿/数据结构 (gitee.com) ⚙️操作环境:Visual Studio 2022 目录 C语言数据结构前言 1. 什么是数据结构&#xff1f; 2. 什么是算…

矩阵的导数运算

1. 标量方程对向量的导数 一维方程f(y)求极值即求导,令 二维方程f(y1,y2)求极值即求偏导,令 如果一个标量方程f(y1,y2,...ym)有m个自变量,求取它的极值就需要求取m组的方程组。当然可以用一种简洁的方式来表达它,比如二维方程f(y1,y2)可以把其中的变量写成向量的形式,此…

sed 命令与正则表达式

sed 是非交互式的编辑器。它不会修改文件&#xff0c;除非使用 shell 重定向来保存结果。默认情况下&#xff0c;所有的输出行都会被打印到屏幕上。 sed 编辑器逐行处理文件&#xff08;或输入&#xff09;&#xff0c;并将结果打印到屏幕上。 具体过程如下&#xff1a;首先 …

Llama 3.1论文中文对照翻译

The Llama 3 Herd of Models 模型群 Llama 3 Llama Team, Al Meta 1 {}^{1} 1 Llama 团队&#xff0c;Meta Al 1 {}^{1} 1 1 {}^{1} 1 A detailed contributor list can be found in the appendix of this paper. 1 {}^{1} 1 详细的贡献者名单可在本文附录中找到。 Mod…

计算机的错误计算(五十七)

摘要 展示 函数的错误计算。 由计算机的错误计算&#xff08;五十五&#xff09;知&#xff0c;国际IEEE 754 标准给出的 函数的定义域是. 那么&#xff0c;在这个实数域范围内&#xff0c;软件的实现效果怎样&#xff1f; 例1. 已知 在数学软件 Maple 中计算 在Maple …

【ML】Auto-encoder

【ML】Auto-encoder 1. Auto-encoder自编码器的基本结构自编码器的特点自编码器的应用场景总结 2. Discrete Representation3. Anomaly Detection3.1 异常检测的特点3.2 常见的异常检测方法3.3 应用场景3.4 总结 1. Auto-encoder 自编码器&#xff08;Auto-encoder&#xff0…

大模型时代的机遇与挑战

从技术突破到技术社会的融合发展 沈向洋 通用人工智能时代&#xff0c;我们应该怎样思考大模型&#xff1f; 思考 1 算力是门槛 算力成长&#xff08;来自EPOCH AI&#xff09; GPT4 2x1025 GPU算力增长 影响算力的两大要素&#xff1a;模型大小&#xff08;参数量N&#x…

cesium自定图弹框

token记得换成您自己的&#xff01;&#xff01;&#xff01; 申请cesium的token 官网【Cesium: The Platform for 3D Geospatial】 pickEllipsoid在加载地形的情况下有一定误差&#xff0c;地形凹凸程度越大&#xff0c;误差越大。 pickPosition在depthTestAgainstTerrain…

★ C++进阶篇 ★ 继承

Ciallo&#xff5e;(∠・ω< )⌒☆ ~ 今天&#xff0c;我将继续和大家一起学习C进阶篇第一章----继承 ~ 目录 一 继承的概念及定义 1.1 继承的概念 1.2 继承定义 1.2.1 定义格式 1.2.2 继承⽗类成员访问⽅式的变化 1.3 继承类模板 二 父类和子类对象赋值兼容转换…

Spring -- 事务

Spring中事务的操作分为两类:(1)编程式事务 – 手动写代码操作事务(2)声明式事务 – 利用注解开启事务和提交事务 1. 编程式事务 准备Controller RestController RequestMapping("/user") public class UserInfoController {Autowiredprivate UserInfoService use…

用Manim创建条形图【BarChart】

BarChart是Manim库中用于创建条形图的函数。它允许用户通过一组值创建一个条形图&#xff0c;其参数可以调整条形的外观和布局。 BarChart(values, bar_namesNone, y_rangeNone, x_lengthNone, y_lengthNone, bar_colors[#003f5c, #58508d, #bc5090, #ff6361, #ffa600],bar_w…

信息安全服务资质CCRC认证需要哪些条件?

CCRC认证是中国信息安全认证中心开展的信息安全服务资质认证&#xff0c;涵盖7个方向&#xff0c;包括安全集成、安全运维、风险评估、应急处理、软件安全开发、灾难备份与恢复、网络安全审计。每个认证方向的资质级别分为一级、二级、三级&#xff0c;一级最高&#xff0c;三级…

网络基础篇~路由、网络类型和相关命令

一、安装Cisco模拟器 功能&#xff1a;Cisco Packet Tracer&#xff0c;是Cisco官方提供的网络模拟器&#xff0c;专门用于教学和学习网络技术。它提供了广泛的Cisco设备模拟&#xff0c;并具有一个直观的界面&#xff0c;可用于创建、配置和模拟网络拓扑。 &#xff08;一&…

基于vue框架的Android共享停车位i00t4(程序+源码+数据库+调试部署+开发环境)系统界面在最后面。

系统程序文件列表 项目功能&#xff1a;用户,车位主,车位出租,车位预约,取消预约 开题报告内容 基于Vue框架的Android共享停车位系统 开题报告 一、研究背景及意义 1.1 研究背景 随着城市化进程的加速&#xff0c;私家车数量急剧增加&#xff0c;停车难问题日益凸显&…

玛雅房产系统源码开发与技术功能解析

引言 随着房地产市场的蓬勃发展&#xff0c;房产管理系统&#xff08;Real Estate Management System, REMS&#xff09;作为提升行业效率、优化资源配置的关键工具&#xff0c;其重要性日益凸显。房产系统源码开发不仅涉及复杂的业务逻辑处理&#xff0c;还融合了先进的软件开…

无人机之热成像篇

一、定义 无人机热成像技术是指将热成像相机安装在无人机云台上&#xff0c;通过无人机的高空飞行能力和云台的稳定性&#xff0c;结合红外热成像技术对目标区域进行非接触式的温度测量和图像采集。该技术利用物体发出的红外辐射来生成图像&#xff0c;通过测量物体表面温度分布…

08、MySQL-事务

目录 1、事务简介 2、事务操作 2.1 方式一 2.2 方式二 3、事务四大特性 4、并发事务问题 5、事务隔离级别 1、事务简介 事务是一组操作的集合&#xff0c;它是一个不可分割的工作单位&#xff0c;事务会把所有的操作作为一个整体一起向系统提交或撤销操作请求&#xff0c…