ProtoBuf快速上手

文章目录

此篇文章实现内容:

  • 对一个通讯录的联系人信息,使用PB进行序列化,并将结果输出
  • 对序列化的内容使用PB进行反序列化,解析联系人信息并输出
  • 联系人信息包含:姓名、年龄

创建 .proto文件

  • 建议文件名采用小写
  • 后缀名为.proto

messgae可以定义属性字段,格式为:字段类型 字段名 = 字段唯一编号

  • 命名规范:全小写,多个字母之间用_连接
  • 字段类型分为标量数据类型和特殊类型
  • 字段唯一编号:用来标识字段,一旦开始使用就不能改变
.proto Type Notes C++ Type
double double
float float
int32 使用变长编码[1]。负数的编码效率较低⸺若字段可能为负值,应使用 sint32 代替 int32
int64 使用变长编码[1]。负数的编码效率较低⸺若字段可能为负值,应使用 sint64 代替 int64
uint32 使用变长编码[1] uint32
uint64 使用变长编码[1] uint64
sint32 使用变长编码[1]。符号整型。负值的编码效率⾼于常规的 int32 类型 int32
sint64 使用变长编码[1]。符号整型。负值的编码效率⾼于常规的 int64 类型 int64
fixed32 定长 4 字节。若值常大于2^28 则会比 uint32 更高效 uint32
fixed64 定长 8 字节。若值常大于2^56 则会比 uint64 更高效 uint64
sfixed32 定长4字节 int32
sfixed64 定长8字节 int64
bool bool
string 包含 UTF-8 和 ASCII 编码的字符串,长度不能超过2^32 string
bytes 可以包含任意的字节序列,长度不超过2^32 string

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

cpp 复制代码
//语法指定行
syntax = "proto3";
//定义声明空间
package contacts;

//定义联系人message
message PeopleInfo
{
    //标识字段编号
    string name = 1;
    int32 age = 2;
}

这里字段唯一编号范围:[1 ~ 536, 870, 911(2^29-1)]

其中19000 ~ 19999不可用,因为protobuf协议实现中,对这些数进行了预留

1 ~ 15字段编号需要一个字节进行编码
6 ~ 2047需要两个字节进行编码。

编码之后的字节不仅仅包含编号,还包含字段类型,所以1 ~ 15要用来标记频繁出现的字段
可以按照protobuf插件,代码会有高亮显示

编译 .proto文件

protobuf 复制代码
protoc --cpp_out=. contacts.proto
  • --cpp_out=.表明编译的proto文件会生成C++的代码,=.表明生成的C++文件在当前目录下
  • contacts.proto表明所依赖的文件

如果不在这个目录下还想对contacts.proto进行编译:

protobuf 复制代码
protoc -I protobuf_10_2/ --cpp_out=protobuf_10_2/ contacts.proto
  • -I protobuf_10_2表示指定的搜索目录

编译后生成的文件

编译之后生成了*.h*.cc文件,分别存放类的声明和类的实现

*.h文件为例:

这里就是生成的getset方法

序列反序列方法:

  • 在序列反序列化方法,都是继承messagemessage继承MessageLite,里面提供了序列反序列化的方法
  • 序列化之后的结果为二进制字节序列
  • 序列化的API都是const成员函数,因为序列化不会改变类对象的内容,而是将序列化的结果保存到函数入参指定地址当中

序列化与反序列化的使用

编写测试文件main.cc

  • 对联系人信息使用PB进行序列化,将结果输出
  • 对序列化内容使用PB进行反序列化,解析联系人信息并输入
cpp 复制代码
#include<iostream>
#include<string>
#include<fstream>
#include"contacts.pb.h"
int main()
{
    std::string people_str;
    //序列化
    {
        contacts::PeopleInfo people;
        people.set_name("张三");
        people.set_age(20);
        if(!people.SerializeToString(&people_str))
        {
            std::cerr << "serialize error" << std::endl;
            return -1;
        }
        std::cout << "serialize success: " << people_str << std::endl;
    }

    //反序列化
    {
        contacts::PeopleInfo people;
        if(!people.ParseFromString(people_str))
        {
            std::cerr << "parse error" << std::endl;
        }
        std::cout << "parse success: " << std::endl;
        std::cout << "name: " << people.name() << std::endl; 
        std::cout << "age: " << people.age() << std::endl; 
    }
    return 0;
}

编译main.cc

c++ 复制代码
g++ -o testPb main.cc contacts.pb.cc -std=c++11 -lprotobuf

也可以使用makefile:

makefile 复制代码
testPb:main.cc contacts.pb.cc
	g++ -o $@ $^ -std=c++11 -lprotobuf
.PHONY:clean
clean:
	rm testPb

相对于jsonxml来说,protobuf编码成了二进制,破解成本较大,所以protobuf编码是较安全的。

ProtoBuf需要依赖通过编译生成的头文件和源文件使用,所以开发人员就无需编写解析协议的代码,大大提高了开发效率。

相关推荐
可均可可1 分钟前
C++之OpenCV入门到提高004:Mat 对象的使用
c++·opencv·mat·imread·imwrite
。puppy8 分钟前
HCIP--3实验- 链路聚合,VLAN间通讯,Super VLAN,MSTP,VRRPip配置,OSPF(静态路由,环回,缺省,空接口),NAT
运维·服务器
颇有几分姿色18 分钟前
深入理解 Linux 内存管理:free 命令详解
linux·运维·服务器
白子寰23 分钟前
【C++打怪之路Lv14】- “多态“篇
开发语言·c++
小芒果_0128 分钟前
P11229 [CSP-J 2024] 小木棍
c++·算法·信息学奥赛
gkdpjj34 分钟前
C++优选算法十 哈希表
c++·算法·散列表
王俊山IT35 分钟前
C++学习笔记----10、模块、头文件及各种主题(一)---- 模块(5)
开发语言·c++·笔记·学习
-Even-38 分钟前
【第六章】分支语句和逻辑运算符
c++·c++ primer plus
EricWang13581 小时前
[OS] 项目三-2-proc.c: exit(int status)
服务器·c语言·前端
我是谁??1 小时前
C/C++使用AddressSanitizer检测内存错误
c语言·c++