Ubuntu下使用protoBuf

news2024/11/16 19:30:03

一、protobuf简介:

1.1 protobuf的定义:

protobuf是用来干嘛的?

protobuf是一种用于 对结构数据进行序列化的工具,从而实现 数据存储和交换。

(主要用于网络通信中 收发两端进行消息交互。所谓的“结构数据”是指类似于struct结构体的数据,可用于表示一个网络消息。当结构体中存在函数指针类型时,直接对其存储或传输相当于是“浅拷贝”,而对其序列化后则是“深拷贝”。)

**序列化:**将结构数据或者对象转换成能够用于存储和传输的格式。
**反序列化:**在其他的计算环境中,将序列化后的数据还原为数据结构和对象。

从“序列化”字面上的理解,似乎使用C语言中的struct结构体就可以实现序列化的功能:将结构数据填充到定义好的结构体中的对应字段即可,接收方再对结构体进行解析。

在单机的不同进程间通信时,使用struct结构体这种方法实现“序列化”和“反序列化”的功能问题不大,但是,在网络编程中,即面向网络中不同主机间的通信时,则不能使用struct结构体,原因在于:

(1)跨语言平台,例如发送方是用C语言编写的程序,接收方是用Java语言编写的程序,不同语言的struct结构体定义方式不同,不能直接解析;

(2)struct结构体存在内存对齐和 CPU不兼容的问题。

因此,在网络编程中,实现“序列化”和“反序列化”功能需要使用通用的组件,如 Json、XML、protobuf 等。

1.2 protobuf的优缺点:

1.2.1 优点:

① 性能高效:
与XML相比,protobuf更小(3 ~ 10倍)、更快(20 ~ 100倍)、更为简单。

**② 语言无关、平台无关:

**protobuf支持Java、C++、Python等多种语言,支持多个平台。

**③ 扩展性、兼容性强:

**只需要使用protobuf对结构数据进行一次描述,即可从各种数据流中读取结构数据,更新数据结构时不会破坏原有的程序。

Protobuf与XML、Json的性能对比:

测试10万次序列化:

img

测试10万次反序列化:

img

1.2.2 缺点:

① 自解释性较差,数据存储格式为二进制,需要通过 .proto 文件才能了解到内部的数据结构;
② 不适合用来对 基于文本的标记文档(如HTML) 建模。

1.3 protobuf中的数据类型限定修饰符:

protobuf 2 中有三种数据类型限定修饰符:

required, optional, repeated

required表示字段必选,optional表示字段可选,repeated表示一个数组类型。

其中, required 和 optional 已在 proto3 弃用了。

1.4 protobuf中常用的数据类型:

bool,		布尔类型

double,		64位浮点数
float,		32位浮点数

int32,		32位整数
int64,		64位整数
uint64,		64位无符号整数
sint32,		32位整数,处理负数效率更高
sint64,		64位整数,处理负数效率更高

string,		只能处理ASCII字符
bytes,		用于处理多字节的语言字符
enum,		枚举类型

二、protobuf下载与安装

GitHub 地址:
https://github.com/protocolbuffers/protobuf

官方文档地址:
https://developers.google.com/protocol-buffers/

Releases 下载地址:
https://github.com/protocolbuffers/protobuf/releases

另外往下可以看到分平台的文件:
在这里插入图片描述
这些是已经 build 好的 Protocol Compiler,可以直接下载了用,也可以自己下载前面源码文件后自己进行 build

自己下载需要的版本protobuf进行编译,成功后会编译出动态库so和protoc编译器(这个也可在以上网址进行下载需要的版本 )

执行protoc编译器,成功后会有如下提示:

[root@linux] protoc --version
libprotoc 3.15.8

三、protobuf的使用流程

1.1 定义.proto文件

使用protobuf时,需要先根据应用需求编写 .proto 文件 定义消息体格式,例如:

test.proto文件如下:

syntax = "proto3";
package tutorial;

option optimize_for = LITE_RUNTIME;

message Person {
	int32 id = 1;
	repeated string name = 2;
}

以–cpp_out上

1、syntax 关键字表示使用的protobuf的版本,如不指定则默认使用 “proto2”

2、package关键字 表示“包”,生成目标语言文件后对应C++中的namespace命名空间,用于防止不同的消息类型间的命名冲突

3、message对应C++中的类, 内部包含各种属性, 其中repeated修饰数据类型表示是一个数组的概念

1.2 .proto文件编译

通过protoc编译器把.protoc文件编译出xxxx.pb.cc和xxxxx.pb.h

格式如下:

[root@linux] protoc -I=$SRC_DIR --cpp_out=$DST_DIR $SRC_DIR/xxx.proto

其中:
$SRC_DIR表示 .proto文件所在的源目录;
–cpp_out=$DST_DIR表示生成目标语言C++代码的目标目录;
xxx.proto表示要对哪个.proto文件进行解析;

以上目录最好使用绝对路径

编译出xxx.pb.cc和xxx.pb.h文件后,使用的话只需要把文件编译进去并链接protobuf动态库即可

类似这种格式:

g++ main_test.cpp xxx.pb.cc -o main_test -lprotobuf

1.3 C++使用protobuf实现序列化的示例

在protobuf源码中的 /examples 目录下有官方提供的protobuf使用示例:addressbook.proto

参考官方示例实现C++使用protobuf进行序列化和反序列化:

addressbook.proto :

syntax = "proto3";
package tutorial;

option optimize_for = LITE_RUNTIME;

message Person {
	string name = 1;
	int32 id = 2;
	string email = 3;

	enum PhoneType {
		MOBILE = 0;
		HOME = 1;
		WORK = 2;
	}
	
	message PhoneNumber {
		string number = 1;
		PhoneType type = 2;
	}

	repeated PhoneNumber phones = 4;
}

生成的addressbook.pb.h 文件内容摘要:

namespace tutorial {
	class Person;
	class Person_PhoneNumber;
};

class Person_PhoneNumber : public MessageLite {
public:
	Person_PhoneNumber();
	virtual ~Person_PhoneNumber();
public:
	//string number = 1;
	void clear_number();
	const string& number() const;
	void set_number(const string& value);
	
	//int32 id = 2;
	void clear_id();
	int32 id() const;
	void set_id(int32 value);

	//string email = 3; 
	//...
};

add_person.cpp :

#include <iostream>
#include <fstream>
#include <string>
#include "pbs/addressbook.pb.h"
using namespace std;

void serialize_process() {
	cout << "serialize_process" << endl;
	tutorial::Person person;
	person.set_name("Obama");
	person.set_id(1234);
	person.set_email("1234@qq.com");

	tutorial::Person::PhoneNumber *phone1 = person.add_phones();
	phone1->set_number("110");
	phone1->set_type(tutorial::Person::MOBILE);

	tutorial::Person::PhoneNumber *phone2 = person.add_phones();
	phone2->set_number("119");
	phone2->set_type(tutorial::Person::HOME);

	fstream output("person_file", ios::out | ios::trunc | ios::binary);

	if( !person.SerializeToOstream(&output) ) {
		cout << "Fail to SerializeToOstream." << endl;
	}

	cout << "person.ByteSizeLong() : " << person.ByteSizLong() << endl;
}

void parse_process() {
	cout << "parse_process" << endl;
	tutorial::Person result;
	fstream input("person_file", ios::in | ios::binary);

	if(!result.ParseFromIstream(&input)) {
		cout << "Fail to ParseFromIstream." << endl;
	}

	cout << result.name() << endl;
	cout << result.id() << endl;
	cout << result.email() << endl;
	for(int i = 0; i < result.phones_size(); ++i) {
		const tutorial::Person::PhoneNumber &person_phone = result.phones(i);

		switch(person_phone.type()) {
			case tutorial::Person::MOBILE :
				cout << "MOBILE phone : ";
				break;
			case tutorial::Person::HOME :
				cout << "HOME phone : ";
				break;
			case tutorial::Person::WORK :
				cout << "WORK phone : ";
				break;
			default:
				cout << "phone type err." << endl;
		}
		cout << person_phone.number() << endl;
	}
}

int main(int argc, char *argv[]) {
	serialize_process();
	parse_process();
	
	google::protobuf::ShutdownProtobufLibrary();	//删除所有已分配的内存(Protobuf使用的堆内存)
	return 0;
}

输出结果:

[serialize_process]
person.ByteSizeLong() : 39
[parse_process]
Obama
1234
1234@qq.com
MOBILE phone : 110
HOME phone : 119

分析

protobuf提供的序列化和反序列化的API接口函数:

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

四、拓展: .proto文件中的 option 选项:

.proto文件中的option选项用于配置protobuf编译后生成目标语言文件中的代码量,可设置为 SPEEDCODE_SIZELITE_RUNTIME 三种。
默认option选项为 SPEED,常用的选项为 LITE_RUNTIME。

三者的区别在于:

① SPEED(默认值):
表示生成的代码运行效率高,但是由此生成的代码编译后会占用更多的空间。

② CODE_SIZE:
与SPEED恰恰相反,代码运行效率较低,但是由此生成的代码编译后会占用更少的空间,通常用于资源有限的平台,如Mobile。

③ LITE_RUNTIME:
生成的代码执行效率高,同时生成代码编译后的所占用的空间也非常少。
这是以牺牲Protobuf提供的反射功能为代价的。
因此我们在C++中链接Protobuf库时仅需链接libprotobuf-lite,而非protobuf。

SPEED 和 LITE_RUNTIME相比,在于调试级别上,例如 msg.SerializeToString(&str); 在 SPEED 模式下会利用反射机制打印出详细字段和字段值,但是 LITE_RUNTIME 则仅仅打印字段值组成的字符串。

因此:可以在调试阶段使用 SPEED 模式,而上线以后提升性能使用 LITE_RUNTIME 模式优化。

最直观的区别是使用三种不同的 option 选项时,编译后产生的 .pb.h 中自定义的类所继承的 protobuf类不同:

//1. SPEED模式:(自定义的类继承自 Message 类)
// .proto 文件:
option optimize_for = SPEED;
// .pb.h 文件:
class Person : public ::PROTOBUF_NAMESPACE_ID::Message {};

//2. CODE_SIZE模式:(自定义的类继承自 Message 类)
// .proto 文件:
option optimize_for = CODE_SIZE;
// .pb.h 文件:
class Person : public ::PROTOBUF_NAMESPACE_ID::Message {};

//3. LITE_RUNTIME模式:(自定义的类继承自 MessageLite 类)
// .proto 文件:
option optimize_for = LITE_RUNTIME;
// .pb.h 文件:
class Person : public ::PROTOBUF_NAMESPACE_ID::MessageLite {};

参考:

1、https://zhuanlan.zhihu.com/p/594534435?utm_id=0

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

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

相关文章

亚马逊运营中动态/静态住宅IP代理的应用有哪些?

作为全球最大的电商平台之一&#xff0c;亚马逊已经成为许多商家的首选销售平台。而代理IP作为近几天互联网的热门工具&#xff0c;在跨境电商界也起着非常强大的作用。那么在亚马逊运营中&#xff0c;适合动态住宅代理还是静态住宅代理呢&#xff1f;下面我们一起来探索&#…

数据挖掘之PCA-主成分分析

PCA的用处&#xff1a;找出反应数据中最大变差的投影&#xff08;就是拉的最开&#xff09;。 在减少需要分析的指标同时&#xff0c;尽量减少原指标包含信息的损失&#xff0c;以达到对所收集数据进行全面分析的目的 但是什么时候信息保留的最多呢&#xff1f;具体一点&#…

Linux:Ubuntu系统安装软件

本次以安装vim为例 sudo apt-get remove vim //卸载vim sudo apt-get install vim //安装vim sudo apt-cache show vim //获取vim软件信息安装时间较长。 安装完成后&#xff0c;执行下第三条指令&#xff0c;测试下是否安装成功即可。

【element优化经验】el-dialog修改title样式

目录 前言 解决之路 1.把默认的这个图标隐藏&#xff0c;官方的api有这个属性&#xff1a;showClose值设置false. 2.title插槽定制&#xff1a;左边定制标题&#xff0c;右边定制按钮区域。 3.背景颜色修改&#xff1a;默认title是有padding的需要把它重写调&#xff0c;然…

Ubuntu 安装 JMeter:轻松上手

Apache JMeter 是一个开源的负载测试工具&#xff0c;可以用于测试静态和动态资源&#xff0c;确定服务器的性能和稳定性。在本文中&#xff0c;我们将讨论如何下载和安装 JMeter。 安装 Java&#xff08;已安装 Java 的此步骤可跳过&#xff09; 要下载 Java&#xff0c;请遵…

Redis中文结果查看方式

背景 当使用redis时我们存储到缓存中可能会包含一些中文,例如下面命令 set test 中国 当执行查看时,发现客户端显示的并不是中文而是乱码,例如下面结果 get test \xe4\xb8\xad\xe5\x9b\xbd 现对【\xe4\xb8\xad\xe5\x9b\xbd】的查看有如下几个方式 方式一:通过客户端直…

井盖位移传感器厂家批发,守护井盖安全

窨井盖广泛分布于城市街道&#xff0c;其管理效果直接反映了城市治理的现代化程度。根据住房和城乡建设部发布的《关于进一步加强城市窨井盖安全管理的通知》&#xff0c;全国各地需加强窨井盖的安全管理。作为市政基础设施的一个重要的组成部分&#xff0c;井盖的管理工作不仅…

FindMy技术用于旅行箱

旅行箱&#xff0c;那是出门在外的我们不可或缺的伙伴。无论是商务出差&#xff0c;还是短途旅行&#xff0c;亦或是长途度假&#xff0c;旅行箱都以其便捷的方式&#xff0c;陪伴着我们的整个行程。 然而&#xff0c;在旅途中&#xff0c;丢失旅行箱是一件非常棘手的问题&…

Spring Cloud LoadBalancer 简单介绍与实战

前言 本文为SpringCloud的学习笔记&#xff0c;如有错误&#xff0c;希望各位高手能指出&#xff0c;主要介绍SpringCloudLoadBalancer的基本概念和实战 文章目录 前言什么是LoadBalancer负载均衡分类服务端负载均衡客户端负载均衡服务端负载均衡和客户端负载均衡的优缺点 常见…

主播产品转场(款)话术

直播转场话术要点 在直播过程中&#xff0c;转场话术是非常重要的一部分。它可以帮助主播J顺利地将一个主题或场景过渡到另一个主题或场景&#xff0c;同时吸引观众的注意力。提高直播的观赏性和互动性。以下是一些直播转场话术的要点: 一、过渡性话语 过渡性话语是连接两个…

交叉导轨在光学工作台起什么重要作用?

光学工作台常常需要承载和移动各种光学元件和仪器&#xff0c;如望远镜、显微镜、光谱仪等&#xff0c;这些设备需要在空间中进行精确的定位和稳定支撑&#xff0c;而交叉导轨作为一种高精度、高刚度的直线传动元件&#xff0c;为光学工作台提供了重要的支撑和导向。 1>交叉…

Springboot将多个图片导出成zip压缩包

Springboot将多个图片导出成zip压缩包 将多个图片导出成zip压缩包 /*** 判断时间差是否超过6小时* param startTime 开始时间* param endTime 结束时间* return*/public static boolean isWithin6Hours(String startTime, String endTime) {// 定义日期时间格式DateTimeFormatt…

140. 单词拆分 II

140. 单词拆分 II Java错误代码&#xff1a;不该回溯数组的&#xff0c;回溯数组是以固定顺序来的&#xff0c;应该回溯字符串&#xff01; class Solution {StringBuilder sb;List<String> list;List<String> tmp;private String getString() {StringBuilder str…

Chrome网页前端组件调试模式,获取核心业务逻辑

进入网页&#xff0c;点击F12&#xff0c;弹出开发者工具对话框&#xff0c;如下图 定位目标组件&#xff0c;如按钮&#xff0c;修改html&#xff0c;插入οnclick"debugger"代码 在网页点击该按钮&#xff0c;触发调试模式 不停按F11&#xff0c;逐个检索文件…

Flink Flink中的分流

一、什么是分流 所谓“分流”&#xff0c;就是将一条数据流拆分成完全独立的两条、甚至多条流。也就是基于一个DataStream&#xff0c;定义一些筛选条件&#xff0c;将符合条件的数据拣选出来放到对应的流里。 二、基于filter算子的简单实现分流 其实根据条件筛选数据的需求…

Windows核心编程 进程

目录 一、进程概述 二、创建进程相关API Winexec ShellExecute CreateProcess 三、进程退出相关API ExitProcess TerminateProcess GetCurrentProcess GetExitCodeProcess 四、如何理解虚拟内存空间 五、关于UAC 一、进程概述 进程&#xff1a;正在运行的程序 程…

虾皮插件:优化Shopee商家店铺运营的利器

在如今竞争激烈的电商市场中&#xff0c;如何提升Shopee商家店铺的运营效率和销售业绩成为了摆在每个商家面前的一道难题。然而&#xff0c;幸运的是&#xff0c;虾皮插件-知虾的出现为商家们带来了一种全新的解决方案。本文将介绍虾皮插件的用途和优势&#xff0c;并详细介绍其…

Unity Android FireBase bugly报错查询

报错如下图&#xff0c;注意&#xff0c;标红的三处 使用的il2cpp和架构是arm64-v8a 那我们就可以根据这些去找对应的符号表&#xff0c;在unity安装目录下 Unity2020.3.33f1\Editor\Data\PlaybackEngines\AndroidPlayer\Variations\il2cpp\Release\Symbols\arm64-v8a 找到l…

NVMe-oF E-JBOF设计解析:WD RapidFlex网卡、OpenFlex Data24

OpenFlex Data24 NVMe-oF Storage Platform WD的SN840 NVMeSSD新品并没有太吸引我注意&#xff0c;因为它还是PCIe 3.0接口的&#xff0c;要知道Intel的PCIe 4.0 SSD都已经推出了。 但上面这个NVMe-oF&#xff08;NVMe over Fabric&#xff09;EBOF&#xff08;区别于普通JBO…

评测|PolarDB MySQL 版 Serverless

评测&#xff5c;PolarDB MySQL 版 Serverless 目录 一、测试背景 1.1、云原生数据库 PolarDB Serverless新架构概念 1.2、Serverless资源弹性扩缩触发条件 二、PolarDB的Serverless能力与同类型产品进行对比 三、动态弹性升降资源的能力测试 3.1、测试资源 3.2、测试一…