protobuf_1

Catalogue
  1. 1. 1. Protobuf
  2. 2. 2. Protobuf-Tutorials 应用举例
    1. 2.1. 2.1. 定义格式
    2. 2.2. 2.2. addressBook.proto
    3. 2.3. 2.3. 编译protobuf,生成消息类
      1. 2.3.1. 2.3.1. addressbook.pb.h
    4. 2.4. 2.4. 解析和序列化
    5. 2.5. 2.5. C++调用
      1. 2.5.1. 2.5.1. 示例:将个人详细信息写入地址簿文件
        1. 2.5.1.1. 2.5.1.1. 运行效果
      2. 2.5.2. 2.5.2. 示例:从地址簿文件读取信息
        1. 2.5.2.1. 2.5.2.1. 运行效果

1. Protobuf

Protobuf是一种平台无关、语言无关、可扩展且轻便高效的序列化数据结构的协议,可以用于网络通信和数据存储

2. Protobuf-Tutorials 应用举例

Tutorials示例:一个非常简单的“地址簿”应用程序,它可以在文件中读取和写入人们的联系方式。通讯录中的每个人都有一个姓名,一个ID,一个电子邮件地址和一个联系电话

  • .proto 文件中定义消息格式
  • 使用protocol buffer编译器
  • 使用C++ protocol buffer API读写消息

使用protobuf,可以编写.proto文件来描述要储存的数据结构,protobuf编译器会创建一个类,该类以有效的二进制格式实现协议缓冲区数据的自动编码和解析。生成的类为构成协议缓冲区的字段提供获取器设置器,并以协议为单位来详细阅读和写入协议缓冲区。重要的是,协议缓冲区格式支持随时间扩展格式的想法,以使代码仍可以读取以旧格式编码的数据。

2.1. 定义格式

为了创建“地址簿”应用程序,需要从.proto 文件开始。.proto 中的定义很简单:把要序列化的每个数据结构添加一条nessage,然后为消息中的每个字段指定名称和类型。

2.2. addressBook.proto

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
syntax = "proto3";
package tutorial;

import "google/protobuf/timestamp.proto";
// [END declaration]

// [START java_declaration]
option java_package = "com.example.tutorial";
option java_outer_classname = "AddressBookProtos";
// [END java_declaration]

// [START csharp_declaration]
option csharp_namespace = "Google.Protobuf.Examples.AddressBook";
// [END csharp_declaration]

// [START messages]
message Person {
string name = 1;
int32 id = 2; // Unique ID number for this person.
string email = 3;

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

message PhoneNumber {
string number = 1;
PhoneType type = 2;
}

repeated PhoneNumber phones = 4;

google.protobuf.Timestamp last_updated = 5;
}

// Our address book file is just one of these.
message AddressBook {
repeated Person people = 1;
}
// [END messages]
}

该.proto文件以程序包声明开头,这有助于防止不同项目之间的命名冲突。在C ++中,您生成的类将放置在与程序包名称匹配的名称空间中。

在上述示例中:

  • Person消息包含PhoneNumber消息,
  • 而AddressBook消息包含Person消息
  • 甚至可以定义嵌套在其他消息中的消息类型
  • 每个元素上的“ = 1”,“ = 2”标记标识该字段在二进制编码中使用的唯一“标记”

2.3. 编译protobuf,生成消息类

既然有了.proto,接下来需要做的是生成读取和写入AddressBook(Person和PhoneNumber)消息所需的类。

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

执行上述编译指令之后,将会在目的目录得到消息类文件:

  • addressbook.pb.h
  • addressbook.pb.cc

2.3.1. addressbook.pb.h

对于上述定义的字段,可以看到生成了这些类:

1
2
3
4
5
6
7
8
9
10
11
12

namespace tutorial {
class AddressBook;
class AddressBookDefaultTypeInternal;
extern AddressBookDefaultTypeInternal _AddressBook_default_instance_;
class Person;
class PersonDefaultTypeInternal;
extern PersonDefaultTypeInternal _Person_default_instance_;
class Person_PhoneNumber;
class Person_PhoneNumberDefaultTypeInternal;
extern Person_PhoneNumberDefaultTypeInternal _Person_PhoneNumber_default_instance_;
} // namespace tutorial

对应的UML类图

2.4. 解析和序列化

  • bool SerializeToString(string* output) const;:序列化消息并将字节存储在给定的字符串中。请注意,字节是二进制的,而不是文本;我们仅将string类用作方便的容器。
  • bool ParseFromString(const string& data);:解析来自给定字符串的消息
  • bool SerializeToOstream(ostream* output) const;: 将消息写入给定的C ++ ostream
  • bool ParseFromIstream(istream* input);: 解析来自给定C ++的消息istream

2.5. C++调用

2.5.1. 示例:将个人详细信息写入地址簿文件

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
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
#include <ctime>
#include <fstream>
#include <google/protobuf/util/time_util.h>
#include <iostream>
#include <string>

#include "addressbook.pb.h"

using namespace std;

using google::protobuf::util::TimeUtil;

void PromptForAddress(tutorial::Person* person) {
//设置ID
cout << "Enter person ID number: ";
int id;
cin >> id;
person->set_id(id);
cin.ignore(256, '\n');

//设置name
cout << "Enter name: ";
getline(cin, *person->mutable_name());

//设置email
cout << "Enter email address (blank for none): ";
string email;
getline(cin, email);
if (!email.empty()) {
person->set_email(email);
}

//设置phone
while (true) {
cout << "Enter a phone number (or leave blank to finish): ";
string number;
getline(cin, number);
if (number.empty()) {
break;
}

tutorial::Person::PhoneNumber* phone_number = person->add_phones();
phone_number->set_number(number);

cout << "Is this a mobile, home, or work phone? ";
string type;
getline(cin, type);
if (type == "mobile") {
phone_number->set_type(tutorial::Person::MOBILE);
} else if (type == "home") {
phone_number->set_type(tutorial::Person::HOME);
} else if (type == "work") {
phone_number->set_type(tutorial::Person::WORK);
} else {
cout << "Unknown phone type. Using default." << endl;
}
}
//时间戳
*person->mutable_last_updated() = TimeUtil::SecondsToTimestamp(time(NULL));
}

int main(int argc, char* argv[]) {
// 宏定义用于确定protobuf版本,版本不一致则抛出异常
GOOGLE_PROTOBUF_VERIFY_VERSION;

//输入一个文件,如果没有,则会新建一个
if (argc != 2) {
cerr << "Usage: " << argv[0] << " ADDRESS_BOOK_FILE" << endl;
return -1;
}
//这是由.proto编译生成的类
tutorial::AddressBook address_book;

//尝试从已有数据文件读取,没有则创建
{
// Read the existing address book.
fstream input(argv[1], ios::in | ios::binary);
if (!input) {
cout << argv[1] << ": File not found. Creating a new file." << endl;
} else if (!address_book.ParseFromIstream(&input)) {
cerr << "Failed to parse address book." << endl;
return -1;
}
}

//地址簿新增一个人的信息
PromptForAddress(address_book.add_people());
//保存到文件
{
//准备输出
fstream output(argv[1], ios::out | ios::trunc | ios::binary);
// 序列化输出
if (!address_book.SerializeToOstream(&output)) {
cerr << "Failed to write address book." << endl;
return -1;
}
}

// Optional: Delete all global objects allocated by libprotobuf.
google::protobuf::ShutdownProtobufLibrary();

return 0;
}

CMakeList.txt

1
2
3
4
5
6
7
8
9
10
11
12
13
14
cmake_minimum_required(VERSION 2.8)

project(protobuf3_learn)
set(CMAKE_INCLUDE_CURRENT_DIR TRUE)

# Find required protobuf package
find_package(protobuf CONFIG REQUIRED)

if(protobuf_VERBOSE)
message(STATUS "Using Protocol Buffers ${Protobuf_VERSION}")
endif()

add_executable(addFile addressbook.pb.cc add_person.cc )
target_link_libraries(addFile protobuf::libprotobuf)

2.5.1.1. 运行效果

2.5.2. 示例:从地址簿文件读取信息

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
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
// See README.txt for information and build instructions.

#include <fstream>
#include <google/protobuf/util/time_util.h>
#include <iostream>
#include <string>

#include "addressbook.pb.h"

using namespace std;

using google::protobuf::util::TimeUtil;

//读取信息
void ListPeople(const tutorial::AddressBook& address_book) {
for (int i = 0; i < address_book.people_size(); i++) {
const tutorial::Person& person = address_book.people(i);

cout << "Person ID: " << person.id() << endl;
cout << " Name: " << person.name() << endl;
if (person.email() != "") {
cout << " E-mail address: " << person.email() << endl;
}

for (int j = 0; j < person.phones_size(); j++) {
const tutorial::Person::PhoneNumber& phone_number = person.phones(j);

switch (phone_number.type()) {
case tutorial::Person::MOBILE:
cout << " Mobile phone #: ";
break;
case tutorial::Person::HOME:
cout << " Home phone #: ";
break;
case tutorial::Person::WORK:
cout << " Work phone #: ";
break;
default:
cout << " Unknown phone #: ";
break;
}
cout << phone_number.number() << endl;
}
if (person.has_last_updated()) {
cout << " Updated: " << TimeUtil::ToString(person.last_updated()) << endl;
}
}
}

// Main function: Reads the entire address book from a file and prints all
// the information inside.
int main(int argc, char* argv[]) {
// Verify that the version of the library that we linked against is
// compatible with the version of the headers we compiled against.
GOOGLE_PROTOBUF_VERIFY_VERSION;

if (argc != 2) {
cerr << "Usage: " << argv[0] << " ADDRESS_BOOK_FILE" << endl;
return -1;
}

tutorial::AddressBook address_book;

{
// Read the existing address book.
fstream input(argv[1], ios::in | ios::binary);
if (!address_book.ParseFromIstream(&input)) {
cerr << "Failed to parse address book." << endl;
return -1;
}
}

ListPeople(address_book);

// Optional: Delete all global objects allocated by libprotobuf.
google::protobuf::ShutdownProtobufLibrary();

return 0;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
cmake_minimum_required(VERSION 2.8)

project(protobuf3_learn)
set(CMAKE_INCLUDE_CURRENT_DIR TRUE)

# Find required protobuf package
find_package(protobuf CONFIG REQUIRED)

if(protobuf_VERBOSE)
message(STATUS "Using Protocol Buffers ${Protobuf_VERSION}")
endif()


add_executable(addFile addressbook.pb.cc add_person.cc )
add_executable(readFile addressbook.pb.cc list_people.cc)
target_link_libraries(addFile protobuf::libprotobuf)
target_link_libraries(readFile protobuf::libprotobuf)

2.5.2.1. 运行效果