Protobuf学习笔记

Protocol Buffers是一种序列化数据结构的协议。对于透过管道(pipeline)或存储资料进行通信的程序开发上是很有用的。这个方法包含一个接口描述语言,描述一些数据结构,并提供程序工具根据这些描述产生代码,用于将这些数据结构产生或解析资料流。

安装配置

protobuf的github发布地址: https://github.com/protocolbuffers/protobuf/releases

protobuf的编译器叫protoc,在上面的网址中找到最新版本的安装包,下载安装。

这里下载的是:protoc-3.9.1-win64.zip , windows 64位系统版本的编译器,下载后,解压到你想要的安装目录即可。

提示:解压完成后,将 [protoc安装目录]/bin 路径添加到PATH环境变量中

打开cmd,命令窗口执行protoc命令,没有报错的话,就已经安装成功。

源码编译

因为需要protobuf的header、lib、dll用于测试,需要编译protobuf的源代码。请参考:

如果使用qt进行开发,请参考:

注意:项目依赖库使用的编译链接工具应与项目使用的编译链接工具相同。例如:都使用 msvc2017 64编译链接。或者考虑跨平台使用mingw 64编译链接。

创建 .proto 文件,定义数据结构

proto3 的语法请参考:

proto3 与 proto2 的区别请参考:

下面是一个proto简单示例:

1
2
3
4
5
6
7
8
9
10
11
// 指定protobuf的版本,proto3是最新的语法版本
syntax = "proto3";

package dream;

message helloworld
{
int32 id = 1; // ID
string str = 2; // str
int32 opt = 3; //optional field
}

将.proto文件,编译成指定语言类库

1
protoc -I=$SRC_DIR --cpp_out=$DST_DIR $SRC_DIR/addressbook.proto

在生成的头文件中添加如下宏定义:

1
#define PROTOBUF_USE_DLLS

测试程序

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
// TestProtoBuffer.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。
//

#include <iostream>
#include <fstream>
#include "MessageStruct.pb.h"

int main()
{
Msg_Header msgHeader;
msgHeader.set_msgtype(1);
msgHeader.set_msgsubtype(2);

// Write the new address book back to disk.
fstream output("./log", ios::out | ios::trunc | ios::binary);

if (!msgHeader.SerializeToOstream(&output)) {
cerr << "Failed to write msg." << endl;
return -1;
}

Msg_Header msgHeaderFromFile1;
try {
string work_dir = "E:\\Target";
fstream input(work_dir+"\\Debug\\log", ios::in | ios::binary);
if (!msgHeaderFromFile1.ParseFromIstream(&input)) {
cerr << "Failed to parse address book." << endl;
return -1;
}
}
catch (exception ex) {
cout << ex.what() << endl;
}

std::cout << "文件反序列化!\n";
cout << msgHeaderFromFile1.msgtype() << endl;
cout << msgHeaderFromFile1.msgsubtype() << endl;

string testStr;
msgHeader.SerializeToString(&testStr);
Msg_Header msgHeaderFromFile2;
msgHeaderFromFile2.ParseFromString(testStr);

std::cout << "\n字符串反序列化!\n";
cout << msgHeaderFromFile2.msgtype() << endl;
cout << msgHeaderFromFile2.msgsubtype() << endl;
}

变量赋值

repeated类型变量赋值

使用问题

Several shared object using same proto leading the the error: file already exists in database

解决方案请参考:

protobuf 解析错误 ParseFromArray 时返回false

可能的原因有:

  • 消息没有序列化,因此消息反序列化失败

解决方案请参考:

参考链接

  1. Protobuf 终极教程,by 鸟窝.
  2. Protobuf通信协议详解:代码演示、详细原理介绍等,by CPP加油站.
  3. Protocol Buffers V3中文语法指南[翻译],by 李文周.
  4. Qt中使用Protobuf简单案例(Windows + msvc),by WindSun.
  5. Protobuf 的 proto3 与 proto2 的区别,by huanggang982.
  6. 长文图解Google的protobuf思考、设计、应用,by 嵌入式客栈.
  7. protobuf优缺点及编码原理,by 牛奔.
  8. Protobuf 使用过程遇到的一个问题,by jashwang.