Protobuf 新手教程

Protobuf 是Google官方出品一种轻便高效的结构化数据存储格式,可以用于结构化数据串行化,或者说是序列化。它很适合做数据存储或RPC数据交换格式。可用于通讯协议、数据存储等领域的平台无关、语言无关、可扩展的序列化结构数据格式。

优点

  • 平台无关,语言无关,可扩展

  • 提供了友好的动态库,使用简单

  • 解析速度快,比对应的XML快约20-100倍

  • 序列化数据非常简洁、紧凑,与XML相比,其序列化之后的数据量约为1/3到1/10

缺点

protobuf对于1M以下的message有很高的效率,但是当message是大于1M的大块数据时,protobuf的表现不是很好,请合理使用。

Proto文件

使用 protobuf, 需要先书写 .proto 文件,然后编译该文件。编译 proto 文件则需要使用官方的 protoc 工具。

// Filename: addressbook.proto

syntax="proto2";  // 表明使用protobuf的编译器版本为v2,目前最新的版本为v3   
package addressbook;

message Person {    //message是Protobuf中的结构化数据,类似于C++中的类,可以在其中定义需要处理的数据
    required string name = 1;
    required int32 id = 2;
    optional string email = 3;

    enum PhoneType {
        MOBILE = 0;
        HOME = 1;
        WORK = 2;
    }

    message PhoneNumber {
        required string number = 1;
        optional PhoneType type = 2 [default = HOME];
    }

    repeated PhoneNumber phone = 4;
}

message AddressBook {
    repeated Person person_info = 1;
}

/*

1,2,3 是字段的标识号,在消息定义中,每个字段都有唯一的一个数字标识号,这些标识号是用来在消息的二进制格式中识别各个字段的,一旦开始使用就不能够再改变。
标识号的范围在:1 ~ 229 - 1,其中[19000-19999]为Protobuf预留,不能使用。

Person 内部声明了一个enum和一个message,这类似于C++中的类内声明,Person外部的结构可以用 Person.PhoneType 的方式来使用PhoneType。当使用外部package中的结构时,要使用 pkgName.msgName.typeName 的格式,每两层之间使用'.'来连接,类似C++中的"::"。

*/

标量类型列表

proto类型 C++类型 备注
double double  
float float  
int32 int32 使用可变长编码,编码负数时不够高效——如果字段可能含有负数,请使用sint32
int64 int64 使用可变长编码,编码负数时不够高效——如果字段可能含有负数,请使用sint64
uint32 uint32 使用可变长编码
uint64 uint64 使用可变长编码
sint32 int32 使用可变长编码,有符号的整型值,编码时比通常的int32高效
sint64 int64 使用可变长编码,有符号的整型值,编码时比通常的int64高效
fixed32 uint32 总是4个字节,如果数值总是比总是比228大的话,这个类型会比uint32高效
fixed64 uint64 总是8个字节,如果数值总是比总是比256大的话,这个类型会比uint64高效
sfixed32 int32 总是4个字节
sfixed64 int64 总是8个字节
bool bool  
string string 一个字符串必须是UTF-8编码或者7-bit ASCII编码的文本
bytes string 可能包含任意顺序的字节数据


 

 win10系统下,C++开发如何使用

1、源码安装

访问最新地址,c++ 开发则下载 protobuf-cpp-3.19.1.tar.gz; 或是 protoc-3.19.1-win64.zip

https//github.com/protocolbuffers/protobuf/release/latest

2、解压源码到任意路径下

path/protobuf-3.19.1

3、下载 CMake 工具

4、CMake生成 VS2019 工程

  • 打开CMake

  • 设置源码路径下的cmake目录 path/protobuf-3.19.1/cmake

  • 设置任意构建目录 path/protobuf_build

  • 点击 configure、选择对应 VS,编译为 WINI32,编译器默认

  • 点击Finish按钮,开始自动编译

  • 点击 Generate 生成VS项目

  • 用 VS 打开生成的工程,按需要选择编译 libprotobuf、libprotobuf-lite、libprotoc和protoc项目

  • 在 release 或者 debug目录,找到 lib 文件和 protoc.exe

5、protoc.exe 使用命令行将 .proto 文件编译生成对应的 .h 和 .cc 文件

# 标准命令
protoc -I=./ --cpp_out=./ ./test.proto
#--cpp_out 是输出路径,  ./为当前路径

6、引入自己的 VS2019 工程使用

  • 拷贝 *.pb.cc 和 *.pb.h 文件到当前项目

  • 拷贝 src目录下的goole文件夹到当前项目的include目录下

  • 拷贝 libprotobuf.lib libprotoc.lib 到当前项目的 lib 目录下

  • 项目设置为多字节,添加静态链接库

  • C/C++ General Additional Include Directories 添加include目录

  • Linker General Additional Library Directories 添加 lib 目录

  • Linker Input Additional Dependencies 添加 lib 字段名

7、VS编译或是g++编译生成可执行代码

g++ main.cc xxx.pb.cc -I $INCLUDE_PATH -L $LIB_PATH -lprotobuf -pthread

 

常用API

protoc 为message的每个required字段和optional字段都定义了一下几个函数,不限于这几个

TypeName xxx() const;           //获取字段
// bool has_xxx();              //判断是否设置值   pb 2
void set_xxx(const TypeName&);  //设置
void clear_xxx();               //使其变为默认值

为每个 repeated 字段定义了以下几个:

TypeName* add_xxx();            //增加结点
TypeName xxx(int) const;        //获取指定序号的结点,类似于c++的“[]”运算符
TypeName* mutable_xxx(int);     //类似于上一个,但是获取的是指针
int xxx_size();                 //获取结点的数量

几个常用的序列化函数

bool SerializeToOstream(std::ostream * output) const;   //输出到输出流中
bool SerializeToString(string * output) const;          //输出到string
bool SerializeToArray(void * data, int size) const;     //输出到字节流

与之对应的反序列化函数

bool ParseFromIstream(std::istream * input);            //从输入流解析
bool ParseFromString(const string & data);              //从string解析
bool ParseFromArray(const void * data, int size);        //从字节流解析

其他常用函数

bool IsInitialized();       // 检查是否所有required字段都被设值
size_t ByteSize() const;    // 获取二进制字节序的大小    pb2
官方API文档: https://developers.google.com/protocol-buffers/docs/reference/overview

参考资料

https://www.cnblogs.com/autyinjing/p/6495103.html

 

上一篇:Proto3语法入门


下一篇:Golang - 关于 proto 文件的一点小思考