ProtoBuf快速上手(C++)

在快速上⼿中,会编写第⼀版本的通讯录 1.0。在通讯录 1.0 版本中,将实现:
对⼀个联系⼈的信息使⽤ PB 进⾏序列化,并将结果打印出来。
对序列化后的内容使⽤ PB 进⾏反序列,解析出联系⼈信息并打印出来。
联系⼈包含以下信息: 姓名、年龄。

1.创建.proto文件 

关于文件规范 

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

指定 proto3 语法 

Protocol Buffers 语⾔版本3,简称 proto3,是 .proto ⽂件最新的语法版本。proto3 简化了 ProtocolBuffers 语⾔,既易于使⽤,⼜可以在更⼴泛的编程语⾔中使⽤。它允许你使⽤ Java,C++,Python等多种语⾔⽣成 protocol buffer 代码。
在 .proto ⽂件中,要使⽤ syntax = "proto3"; 来指定⽂件语法为 proto3,并且必须写在除去
注释内容的第⼀⾏。 如果没有指定,编译器会使⽤proto2语法。
在通讯录 1.0 的 contacts.proto ⽂件中,可以为⽂件指定 proto3 语法,内容如下

syntax = "proto3";

package 声明符 

package 是⼀个可选的声明符,能表⽰ .proto ⽂件的命名空间,在项⽬中要有唯⼀性。它的作⽤是为了避免我们定义的消息出现冲突。
在通讯录 1.0 的 contacts.proto ⽂件中,可以声明其命名空间,内容如下:

syntax = "proto3";
package contacts;

定义消息(message) 

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

在⽹络传输中,我们需要为传输双⽅定制协议。定制协议说⽩了就是定义结构体或者结构化数据,
⽐如,tcp,udp 报⽂就是结构化的。
再⽐如将数据持久化存储到数据库时,会将⼀系列元数据统⼀⽤对象组织起来,再进⾏存储
所以 ProtoBuf 就是以 message 的⽅式来⽀持我们定制协议字段,后期帮助我们形成类和⽅法来使
⽤。在通讯录 1.0 中我们就需要为 联系⼈ 定义⼀个 message。

.proto ⽂件中定义⼀个消息类型的格式为:

message 消息类型名{}// 注意用驼峰命名法

syntax = "proto3";
package contacts;// 定义消息人信息
message PeopleInfo{}

定义消息字段 

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

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

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

 

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

更新 contacts.proto (通讯录 1.0),新增姓名、年龄字段:

syntax = "proto3";
package contacts;// 定义消息人信息
message PeopleInfo{string name = 1;uint32 age = 2;
}
在这⾥还要特别讲解⼀下字段唯⼀编号的范围:
1 ~ 536,870,911 (2^29 - 1) ,其中 19000 ~ 19999 不可⽤.
19000 ~ 19999 不可⽤是因为:在 Protobuf 协议的实现中,对这些数进⾏了预留。如果⾮要在.proto⽂件中使⽤这些预留标识号,例如将 name 字段的编号设置为19000,编译时就会报警

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

2.编译 contacts.proto ⽂件,⽣成 C++ ⽂件 

对以下代码进行编译

syntax = "proto3";
package contacts;// 定义消息人信息
message PeopleInfo{string name = 1;uint32 age = 2;
}

 编译命令的格式:
 

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⽂件。

更多的信息我们可以通过 protoc --hlep来查看

这里我们执行命令:

protoc --cpp_out=. contacts.proto

 其中 . 表示在当前目录中生成。

执行完毕后,

就生成了对应的.h和.cc文件

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

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

其中,在.h文件中,可以找到我们之前定义的PeopleInfo的定义

这里可以看到它继承了一个Message类。

再往下翻:

(注意,这里的报错是VS的原因,代码是没有问题的,后续g++编译时可以通过)

这里就可以看到我们当初定义的字段,比如age中有一个 age()方法,这个就是一个类似get的方法,调用后会返回get的值。下面同样还有一个 set_age()方法,同理。

总结: 

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

 

 最重要的序列化和反序列化方法,它放在了在消息类的⽗类MessageLite 中,提供了读写消息实例的⽅法,包括序列化⽅法和反序列化⽅法。

 上图中是我们定义的PeopleInfo的父类,它在一个.h文件中

这是它的路径:

 

这是ProtoBuf给我们实现的。

 刚刚也说了,序列化和反序列的方法还放在了消息类的⽗类MessageLite 中,

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

 

3. 序列化和反序列化的使用

创建⼀个测试⽂件 main.cc,⽅法中我们实现:
对⼀个联系⼈的信息使⽤ PB 进⾏序列化,并将结果打印出来。
对序列化后的内容使⽤ PB 进⾏反序列,解析出联系⼈信息并打印出来

 

代码:

#include <iostream>
#include "contacts.pb.h"using namespace std;int main()
{string people_str; // 将序列化后的二进制数据存在这个string中// 用一个花括号{contacts::PeopleInfo people_info; people_info.set_name("亚索");people_info.set_age(35);if(!people_info.SerializeToString(&people_str)) // 序列化失败就退出{cerr << "序列化error!" << endl;exit(1);}else {cout << "序列化成功: " << people_str << endl;}}{contacts::PeopleInfo people_info; if(!people_info.ParseFromString(people_str)){cerr << "反序列化失败!" << endl;exit(1);}else {cout << "反序列化成功!" << endl;cout << "姓名: " << people_info.name() << endl;cout << "年龄: " << people_info.age() << endl;}}return 0;
}

 执行结果:

在打印序列化内容时,由于是二进制的,所以显示内容是不可预期的。

后面反序列化后,打印的结果是符合预期的。

关于编译时:

-lprotobuf:必加,不然会有链接错误。
-std=c++11:必加,使⽤C++11语法

 

 小结

再回到最初,我们所说的

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

 

总的来说:ProtoBuf 是需要依赖通过编译⽣成的头⽂件和源⽂件来使⽤的。有了这种代码⽣成机制,开发⼈员再也不⽤吭哧吭哧地编写那些协议解析的代码了(⼲这种活是典型的吃⼒不讨好)。

 

 

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

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

相关文章

PHP 方头像转为圆图

业务需要把创建海报上的用户头像由方形转为圆形&#xff0c;前端的样式设置不能用。 故采用GD的函数来对方图进行裁剪处理为圆图。 目录 裁剪函数 本地图片 远程图片 效果 参考文章 总结 裁剪函数 从网上找的一个裁剪图片的函数。 代码如下&#xff1a; /* * 将图片切…

代理IP地址的含义与设置指南‌

在数字化时代&#xff0c;互联网已经成为我们日常生活不可或缺的一部分。然而&#xff0c;在享受互联网带来的便利的同时&#xff0c;我们也面临着隐私泄露、访问限制等问题。代理IP地址作为一种有效的网络工具&#xff0c;能够帮助我们解决这些问题。本文将详细介绍代理IP地址…

基于Java Springboot个人财务APP且微信小程序

一、作品包含 源码数据库设计文档万字PPT全套环境和工具资源部署教程 二、项目技术 前端技术&#xff1a;Html、Css、Js、Vue、Element-ui 数据库&#xff1a;MySQL 后端技术&#xff1a;Java、Spring Boot、MyBatis 三、运行环境 开发工具&#xff1a;IDEA/eclipse 微信…

红队/白帽必经之路(16)——如何用Metasploit 在边路进行信息刺探及爆破登录[既然是红队,那就对自己狠一点!!!]

欢迎各位彦祖与热巴畅游本人专栏与博客 你的三连是我最大的动力 以下图片仅代表专栏特色 [点击箭头指向的专栏名即可闪现] 专栏跑道一 ➡️网络空间安全——全栈前沿技术持续深入学习 专栏跑道二 ➡️ 24 Network Security -LJS ​ ​ ​ 专栏跑道三 ➡️ MYSQL REDIS Advan…

vue实现echarts饼图自动轮播

echarts官网&#xff1a;Examples - Apache ECharts echartsFn.ts 把echarts函数封装成一个文件 import * as echarts from "echarts";const seriesData [{"value": 12,"name": "过流报警"},{"value": 102,"name&qu…

C++之异常智能指针其他

C之异常&智能指针&其他 异常关于函数异常声明异常的优劣 智能指针auto_ptrunique_ptrshared_ptrweak_ptr定制删除器 智能指针的历史与boost库 特殊类单例模式饿汉和懒汉的优缺点 C四种类型转换CIO流结语 异常 try括起来的的代码块中可能有throw一个异常&#xff08;可…

混沌工程/混沌测试/云原生测试/云平台测试

背景 私有云/公有云/混合云等具有复杂&#xff0c;分布式&#xff0c;环境多样性等特点&#xff0c;许多特殊场景引发的线上问题很难被有效发现。所以需要引入混沌工程&#xff0c;建立对系统抵御生产环境中失控条件的能力以及信心&#xff0c;提高系统面对未知风险得能力。 …

Hive学习基本概念

基本概念 hive是什么&#xff1f; Facebook 开源&#xff0c;用于解决海量结构化日志的数据统计。 基于Hadoop的一个数据仓库工具&#xff0c;可以将结构化的数据文件映射为一张表&#xff0c;并提供类SQL查询功能 本质是将HQL转化为MapReduce程序。 Hive处理的数据存储在H…

数据分析流程中的Lambda架构,以及数据湖基于Hadoop、Spark的实现

文章目录 一、Lambda架构1、Lambda的三层架构2、简单解释&#xff1a;3、Lambda架构的优缺点 二、数据湖基于Hadoop、Spark的实现1、架构2、数据管理&#xff08;存储层的辅助功能&#xff09; 一、Lambda架构 1、Lambda的三层架构 Batch View&#xff08;批处理视图层&#…

算法笔记:力扣142.环形链表返回链表入口

该题目通俗来说就是需要返回节点的入口&#xff0c;这点与判断是否有环不同&#xff0c;有环是通过快慢指针的形式来判断&#xff0c;但当快慢指针相等的时候&#xff0c;此时的节点不一定是环的入口节点。所以这题需要注意。 关键API&#xff1a; map.putIfAbsent(key,value)…

医院管理系统

私信我获取源码和万字论文&#xff0c;制作不易&#xff0c;感谢点赞支持。 医院管理系统 摘要 随着信息互联网信息的飞速发展&#xff0c;医院也在创建着属于自己的管理系统。本文介绍了医院管理系统的开发全过程。通过分析企业对于医院管理系统的需求&#xff0c;创建了一个计…

说说Elasticsearch查询语句如何提升权重?

大家好&#xff0c;我是锋哥。今天分享关于【说说Elasticsearch查询语句如何提升权重&#xff1f;】面试题。希望对大家有帮助&#xff1b; 说说Elasticsearch查询语句如何提升权重&#xff1f; 1000道 互联网大厂Java工程师 精选面试题-Java资源分享网 在 Elasticsearch 中&…

【Spring Security框架解析】

文章目录 Spring-security介绍Spring-security认证授权流程认证流程Security流程认证过滤器实现获取UserDetail信息 配置Security Spring-security介绍 Spring Security是一个功能强大且高度可定制的Java安全框架&#xff0c;用于保护基于Spring的应用程序。它提供了全面的安全…

[CISCN 2019华东南]Web11

[CISCN 2019华东南]Web11 给了两个链接但是都无法访问 这里我们直接抓包试一下 我们插入X-Forwarded-For:127.0.0.1 发现可以修改了右上角的IP地址&#xff0c;从而可以进行注入 {$smarty.version} 查看版本号 if标签执行PHP命令 {if phpinfo()}{/if} 查看协议 {if system(…

使用SpringBoot实现邮件发送(QQ邮箱为例)

使用SpringBoot实现邮件发送(QQ邮箱为例) 一、获取授权码 1.首先进入qq邮箱找到设置 2、账号栏目&#xff0c;找到POP3/SMTP服务 并开启服务 3、获取授权码 二、SpringBoot集成邮件发送 1.创建邮件发送服务类 package com.example.demo.service;import org.springframework…

hint: Updates were rejected because the tip of your current branch is behind!

问题 本地仓库往远段仓库推代码时候提示&#xff1a; error: failed to push some refs to 192.168.2.1:java-base/java-cloud.git hint: Updates were rejected because the tip of your current branch is behind! refs/heads/master:refs/heads/master [rejected] (…

基于BM1684的AI边缘服务器-模型转换,大模型一体机(二)

目标追踪 注&#xff1a;所有模型转换都是在docker环境中的 先进入docker 这里我们是要在docker环境里编译的&#xff0c;所以先进入docker :~/tpu-nntc# docker run -v $PWD/:/workspace -it sophgo/tpuc_dev:latest初始化环境 root2bb02a2e27d5:/workspace/tpu-nntc# s…

ROS基本框架2——在ROS开发中创建并使用自定义消息(C++版本)

ROS基本框架2——在ROS开发中创建并使用自定义消息(C++版本) code review! 参考笔记 1.ROS基本框架1——编写简单的发布者和订阅者(C++和Python版本) 2.ROS基本框架2——在ROS开发中创建并使用自定义消息(C++版本) 文章目录 ROS基本框架2——在ROS开发中创建并使用自定义…

实例讲解MATLAB绘图坐标轴标签旋转

在进行绘图时需要在图片上添加上做标轴的标签&#xff0c;但是当数据量比较多时&#xff0c;例如一天24小时的数据&#xff0c;这时把每个小时显示在左边轴的标签上&#xff0c;文字内容放不下&#xff0c;因此需要将坐标轴标签旋转一定的角度&#xff0c;这样可以更好在图形上…

Spark 内存管理机制

Spark 内存管理 堆内内存和堆外内存 作为一个 JVM 进程&#xff0c;Executor 的内存管理建立在 JVM(最小为六十四分之一&#xff0c;最大为四分之一)的内存管理之上&#xff0c;此外spark还引入了堆外内存&#xff08;不在JVM中的内存&#xff09;&#xff0c;在spark中是指不…