Learing——protobuf(一)

目录

前言

一、protobuf的简介

二、编写一个.proto文件

1.选择使用的protobuf版本

2.指定命名空间(package)

3.定义一个“消息”(message)

4.定义消息字段

标量类型:

枚举类型(enum):

多选一类型(oneof):

映射类型(map):

泛型类型(Any):

数组(reapted):

5.写一个简单的.proto文件

6.看看生成的文件中为我们生成了哪些方法

三、使用protpbuf生成的方法

总结


前言

在网络的学习中,我们知道了如果想要进行跨网络通信就必须要定义“协议”,而“协议”可以帮助网络通信的两台主机解析数据,进而实现通信的效果。网路通信的方式有很多,但是都离不开一件事,那就是对数据进行序列化和反序列化。而我们常常听到的Json就是序列化工具的一种,本文要介绍的是另一种由Google开发的一种序列化工具。


一、protobuf的简介

当我们自己手写序列化和反序列化程序的时候,我们常常要做一些比较耗时但没有什么技术含量的工作,比如对一个结构体中的数据赋值、清空等函数的编写。protobuf工具为我们省去了这一过程,我们只需要编写一个后缀名位proto文件指定各个消息的关系即可(protobuf以消息为序列化单位),,而后protobuf会为我们生成两个后缀名分别为pb.cc和pb.h的文件,这两个文件中就包含了一些常见的操作,比如说对数据进行赋值或清空等。而后我们就可以使用这些接口来编写我们的主要代码逻辑了。

图1        protobuf各文件的关系

二、编写一个.proto文件

1.选择使用的protobuf版本

在创建好一个后缀名为.proto的文件后我们要注意的第一个点就是,要在除去注释的第一行指明使用的protobuf版本,如果不对版本进行指明,那么编译器将自动使用“proto2”。

syntax="proto3"

2.指定命名空间(package)

在实现一个项目的时候,我们要对每个“消息”的作用域进行一个规定,也是为了我们代码中减少冲突而必要的一个操作。我们通常使用package声明符进行声明。

3.定义一个“消息”(message)

无论是在网络传输还是在数据持久化存储中,我们都会将数据进行结构化。结构化的数据一方面是为了描述一件复杂的事物(比如说一个“人”往往有多个属性,单一属性是难以描述的)所必需的,另一方面,结构化的数据更方便管理和传输。(在传输和存储的时候,如果数据不以结构化来表示,而是以零散的内置类型来表示,那么当数据丢失时,是较难发现的)。

message 消息名称
{
    //数据....
}

4.定义消息字段

标量类型:

图2        protobuf中的字段类型

注:变成编码指的是:protobuf编码后,该数据类型修饰的数据可能大小变为其他字节数。(当数据用较少字节就能表示的时候,就变小,如果当前数据使用当前字节数不能保存,那么就有可能变大)。

这里还要额外说明几个类型:

这几个类型只做文字说明,后续代码中有体现,这里就不进行代码示例了

枚举类型(enum):

就像C语言中的枚举类型一样,可以用大写字母代之常量,在protobuf中需要注意的是:

①零值常量必须存在且应为枚举中的第一个元素,这是为了兼容proto2的语义

②枚举常量可以嵌套定义

③枚举常量的值应该在32为整数范围内,同时由于①和编码限制负数无效

多选一类型(oneof):

oneof结构中的数据只有一个会被选中,如果多次对该结构变量进行数据输入,以最后一次输入为准。

①该结构中的变量编号不能与已有编号重复

②不能在该结构中使用reapted字段

③多次输入取最后一次输入作为有效输入,并将结构中的其他数据清空

映射类型(map):

map<key,value>

①key是除float和bytes以外的任意标量类型,value可以是任意类型

②map字段不可以用reapted字段修饰

③map中的数据是无序的,但key不可以重复 

泛型类型(Any):

可以在/usr/local/protobuf/include/google/protobuf/该路径下查看所有可定义的类型。

注意:为了避免影响观感该类型将于reapted一同在最后单独演示

数组(reapted):

注意:为了避免影响观感该类型将于reapted一同在最后单独演示

5.写一个简单的.proto文件

syntax="proto3";
package Person;

//性别枚举
enum Gender
{
    MAN=0;
    WOMAN=1;
    SECRECY=2;
}

//定义一个“人信息”消息
message PersonInfo
{
    int32 age=1;
    string ID=3;
    Gender sex=4;
    double height=5;

    //oneof类型
    oneof contact_information
    {
        string qq=8;
        string wechat=9;
        string email=10;
    }
    
    //map类型
    map<string,string> ramark=2;
}

我们编写完该文件之后,我们可以在终端中输入该命令来使用该规则生成.pb.cc和.pb.h文件

protoc --cpp_out=. person.proto
图3        protoc解释
图4        使用protobuf工具生成的文件

6.看看生成的文件中为我们生成了哪些方法

我们点解生成文件中的.h文件

图5        查看生成的.h文件

如果你不喜欢手动滑翻查找所需要的信息,可以使用组合键ctrl+f来打开搜索框,将对应的搜索内容输入到搜索框中查找即可,这里要注意的是如果你要使用组合键查找某一字段的方法最好将搜索框中的内容写为:

[定义的变量][空格]=[空格][变量对应的编号]

图6        使用组合键查找生成的头文件中的方法

三、使用protpbuf生成的方法

请注意:本阶段只对语法进行解析,故不作错误处理。最后在编写一个项目的时候会对错误处理进行添加,在项目中不对可能的错误处理是一个很不好的选择。

#include <iostream>
#include <string>
#include <fstream>
#include "person.pb.h"
using namespace std;


void printperson(Person::PersonInfo& tmp)
{
    cout<<"age: "<<tmp.age()<<endl;
    cout<<"ID: "<<tmp.id()<<endl;
    cout<<"sex: "<<tmp.sex()<<endl;
    cout<<"height: "<<tmp.height()<<endl;
    cout<<"qq: "<<tmp.qq()<<endl;//这里应该做特殊判断,但是我们已经知道到底传入了那个联系方式,这里就不做判断了
    
    for(const auto &e:tmp.ramark())
    {
        cout<<"key: "<<e.first<<"value: "<<e.second<<endl;
    }
}

int main()
{
    //向消息中写入数据
    Person::PersonInfo personin;
    personin.set_age(21);
    personin.set_id("10086");
    personin.set_sex(Person::Gender::SECRECY);
    personin.set_height(170.8);
    personin.set_qq("10089");
    personin.mutable_ramark()->insert({"hello","world"});

    //向二进制文件中写入数据
    fstream write("person.bin",ios::out|ios::trunc|ios::binary);
    personin.SerializeToOstream(&write);
    write.close();

    //从二进制文件中读取数据
    fstream read("person.bin",ios::in|ios::binary);
    Person::PersonInfo personout;
    personout.ParseFromIstream(&read);

    printperson(personout);

    read.close();
    return 0;
}

图7        生成的二进制文件与运行结果

解释:protobuf生成的给变量赋值的方法通常是set_xxx,但是在对map赋值的时候我们使用的是mutable_xxx这个接口的意思是为我们开辟这样一份map空间供我们使用。


总结

本文讲解了一些与protobuf有关的简单操作,下一篇文章将对其他的一些字段与protobuf在网络中如何使用进行介绍。

  • 30
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

木鱼不是木鱼

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值