ProtoBuf的使用

news2024/10/7 16:23:50

目录

1.创建.proto文件

1.1文件规范

1.2添加注释

1.3指定proto3语法

1.4package声明符

1.5定义消息(message)

1.6定义消息字段

2.编译contacts.proto文件

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


1.创建.proto文件

1.1文件规范

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

1.2添加注释

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

1.3指定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"; 

1.4package声明符

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

syntax = "proto3";
package contacts;

1.5定义消息(message)

消息(message) : 要定义的结构化对象,我们可以给这个结构化对象中定义其对应的属性内容。
这里再提一下为什么要定义消息?
在网络传输中,我们需 要为传输双方定制协议。定制协议说白了就是定义结构体或者结构化数据,
比如,tcp, udp 报文就是结构化的。
再比如将数据持久化存储到数据库时,会将一系列元数据统一用对象组织起来,再进行存储。
所以ProtoBuf就是以message的方式来支持我们定制协议字段,后期帮助我们形成类和方法来使
用。在通讯录1.0中我们就需要为联系人定义一个message。
.proto文件中定义一个消息类型的格式为:

message 消息类型名{
}

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

为contacts.proto (通讯录1.0)新增联系人message, 内容如下:

syntax = "proto3";
package contacts;
// 定义联系⼈消息
message PeopleInfo {
}

1.6定义消息字段

在message中我们可以定义其属性字段,字段定义格式为:字段类型字段名=字段唯一编号 ;
●字段名称命名规范:全小写字母,多个字母之间用_连接。
●字段类型分为:标量数据类型和特殊类型(包括枚举、其他消息类型等)。
●字段唯一编号:用来标识字段,一旦开始使用就不能够再改变。

该表格展示了定义于消息体中的标量数据类型,以及编译.proto文件之后自动生成的类中与之对应的字段类型。在这里展示了与C++语言对应的类型。

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

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

syntax = "proto3";
package contacts;

message PeopleInfo {
    string name = 1;
    int32 age = 2;
}

在这里还要特别讲解一下字段唯一 编号的范围:
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文件

编译命令
编译命令行格式为:

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

protoc --cpp_out=. contacts.proto

编译contacts.proto文件后,会生成所选择语言的代码,我们选择的是C++,所以编译后生成了两个
文件: contacts.pb.hI contacts.pb.cc。
对于编译生成的C++代码,包含了以下内容:
●对于每个message,都会生成一个对应的消息类。
●在消息类中,编译器为每个字段提供了获取和设置方法,以及一下其他能够操作字段的方法。
●编辑器会针对于每个.proto文件生成.h和.cc文件,分别用来存放类的声明与类的实现。
contacts.pb.h部分代码展示

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

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

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

main.cc

#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 << endl;
    }

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

代码书写完成后,编译main.cc,生成可执行程序TestProtoBuf :

g++ main.cc contacts.pb.cc -o TestProtoBuf -std=c++11 -lprotobuf 

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

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

由于ProtoBuf是把联系人对象序列化成了二进制序列,这里用string来作为接收二进制序列的容器。
所以在终端打印的时候会有换行等一些乱码显示。
所以相对于xml和JSON来说,因为被编码成二进制,破解成本增大,ProtoBuf 编码是相对安全的。

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

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

相关文章

活动回顾 | 数字外贸私享会【上海站】成功举办

11月17日&#xff0c;由箱讯科技主办的数字外贸高端定制私享会【上海站】成功举办&#xff01;本次会议的主题为“新模式、新商机、新政策”&#xff0c;外贸行业的老板、企业家们齐聚一堂&#xff0c;凝聚共识&#xff0c;共话数字外贸的新趋势和新机遇。 近年来&#xff0c;数…

webpack external 详解

作用&#xff1a;打包时将依赖独立出来&#xff0c;在运行时&#xff08;runtime&#xff09;再从外部获取这些扩展依赖&#xff0c;目的时解决打包文件过大的问题。 使用方法&#xff1a; 附上代码块 config.set(externals, {vue: Vue,vue-router: VueRouter,axios: axios,an…

C语言基本算法之选择排序

目录 概要&#xff1a; 代码如下 运行结果如下 概要&#xff1a; 它和冒泡排序一样&#xff0c;都是把数组元素按顺序排列&#xff0c;但是方法不同&#xff0c;冒泡排序是把较小值一个一个往后面移&#xff0c;选择排序则是直接找出最小值&#xff0c;可以这个说&#xff…

1、数仓模型概述

1、问&#xff1a;什么是数据模型&#xff1f; 数仓领域中的模型指的是数据模型&#xff0c;要和商业分析中的模型不同 数据模型就是数据组织和存储方法&#xff0c;它强调从业务、数据存取和使用的角度合理的存储数据 2、问&#xff1a;模型和表的区别&#xff1f; 表是数据物…

SpringBoot-Docker容器化部署发布

在生产环境都是怎么部署 Spring Boot? 打成 jar 直接一键运行打成 war 扔到 Tomcat 容器中运行容器化部署 一、准备Docker 在 CentOS7 上安装好 Docker 修改 Docker 配置&#xff0c;开启允许远程访问 Docker 的功能&#xff0c;开启方式很简单&#xff0c;修改 /usr/lib/s…

redis--高可用之持久化

redis高可用相关知识 在web服务器中&#xff0c;高可用是指服务器可以正常访问的时间&#xff0c;衡量的标准是在多长时间内可以提供正常服务(99.9%、99.99%、99.999%等等)。 但是在Redis语境中&#xff0c;高可用的含义似乎要宽泛一些&#xff0c;除了保证提供正常服务( 如主…

Openlayer【三】—— 绘制多边形GeoJson边界绘制

1.1、绘制多边形 在绘制多边形和前面绘制线有异曲同工之妙&#xff0c;多边形本质上就是由多个点组成的线然后连接组成的面&#xff0c;这个面就是最终的结果&#xff0c;那么这里使用到的是Polygon对象&#xff0c;而传给这个对象的值也是多个坐标&#xff0c;坐标会一个个的…

华为ac+fit漫游配置案例

Ap漫游配置: 其它配置上面一样,ap管理dhcp和业务dhcp全在汇聚交换机 R1: interface GigabitEthernet0/0/0 ip address 11.1.1.1 255.255.255.0 ip route-static 12.2.2.0 255.255.255.0 11.1.1.2 ip route-static 192.168.0.0 255.255.0.0 11.1.1.2 lsw1: vlan batch 100 200…

《栈和队列》的模拟实现(顺序栈) (链队列)

目录 前言&#xff1a; 栈和队列&#xff1a; 栈&#xff1a; 队列&#xff1a; 模拟实现《栈》&#xff1a; 1.typedef数据类型 2.初始化栈 3.销毁栈 4.入栈 5.出栈 6.取栈顶元素 7.判断栈是否为空 8.栈的大小 9.打印栈 模拟实现《队列》 &#xff1a; 1.type…

基于C#实现KMP算法

一、BF 算法 如果让你写字符串的模式匹配&#xff0c;你可能会很快的写出朴素的 bf 算法&#xff0c;至少问题是解决了&#xff0c;我想大家很清楚的知道它的时间复杂度为 O&#xff08;MN&#xff09;&#xff0c;原因很简单&#xff0c;主串和模式串失配的时候&#xff0c;我…

做黄金代理可以代理什么品种?

近几年&#xff0c;黄金代理这个职业发展的比较迅猛&#xff0c;主要是受金融环境越来越稳定、金融投资越来越发达的大势所推动。那些有意想做黄金代理的朋友就会有疑问&#xff0c;做了黄金代理可以代理什么品种的&#xff1f; 其实广义上来说&#xff0c;黄金代理有很多种&am…

【Pytorch】Visualization of Feature Maps(1)

学习参考来自 CNN可视化Convolutional Featureshttps://github.com/wmn7/ML_Practice/blob/master/2019_05_27/filter_visualizer.ipynb 文章目录 filter 的激活值 filter 的激活值 原理&#xff1a;找一张图片&#xff0c;使得某个 layer 的 filter 的激活值最大&#xff0c…

C#核心笔记——(二)C#语言基础

一、C#程序 1.1 基础程序 using System; //引入命名空间namespace CsharpTest //将以下类定义在CsharpTest命名空间中 {internal class TestProgram //定义TestProgram类{public void Test() { }//定义Test方法} }方法是C#中的诸多种类的函数之一。另一种函数*&#xff0c;还…

机器学习介绍与分类

随着科学技术的不断发展&#xff0c;机器学习作为人工智能领域的重要分支&#xff0c;正逐渐引起广泛的关注和应用。本文将介绍机器学习的基本概念、原理和分类方法&#xff0c;帮助读者更好地理解和应用机器学习技术。 一、机器学习的基本概念 机器学习是一种通过从数据中学…

每日汇评:美日在两个月低点附近似乎较为脆弱,熊市可能会在FOMC会议纪要公布前暂停

美元/日元跌至两个月低点&#xff0c;并受到多种因素的压力&#xff1b; 美联储鸽派预期和美国债券收益率下降继续令美元承压&#xff1b; 美日利差缩小以及日本央行政策转变的押注提振了日元&#xff1b; 美元/日元货币对在周二持续第四天承受着沉重的卖压&#xff0c;同时也标…

Vue+Swiper实现轮播图效果

效果展示 实现了自带切换按钮在图片外部实现了自定义的切换按钮 背景 在项目中使用到了轮播图&#xff0c;实现点击上一张下一张时实现循环显示&#xff0c;同时预览两个图片&#xff0c;并加以文字对图片的说明。 设计 使用 Swiper 插件&#xff0c;可以实现当前这个需求。…

linux制作 ext4镜像image 脚本demo

结构如下&#xff1a; build_linux_targetfs.sh #!/bin/bashCHECK_MARK"\033[0;32m\xE2\x9C\x94\033[0m" X_MARK"\033[0;1;31mX\033[0m"export TOP_DIR$PWD export TARGET_IMAGE_PATH$TOP_DIR/filesystem/targetfs-images export BSP_IMAGE_PATH${TOP_DI…

数据库表的内连接和外连接

1.内连接查询语法 -- 隐式内链接 SELECT 字段列表 FROM 表1&#xff0c;表2WHERE 条件&#xff1b; -- 显示内连接 select 字段列表 from 表1 [inner] join 表2 on 条件&#xff1b; 如果两个表没用进行内连接&#xff0c;会生成笛卡尔积。A集合和B集合全部元素进行排列组合。 …

关于校园网使用罗技flow功能

目录 情况概述问题及解决方案 情况概述 我目前设备是一台Macbook air m1处理器&#xff0c;学校给配了一台windows台式&#xff0c;台式机不能连蓝牙&#xff0c;不能连wifi&#xff0c;只能用网线&#xff0c;我的需求是想让mac和windows共用一套键鼠&#xff0c;在了解到罗技…