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" ;option java_package = "com.example.tutorial" ;option java_outer_classname = "AddressBookProtos" ;option csharp_namespace = "Google.Protobuf.Examples.AddressBook" ;message Person { string name = 1 ; int32 id = 2 ; 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 ; } message AddressBook { repeated Person people = 1 ; } }
该.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_;}
对应的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) { cout << "Enter person ID number: " ; int id; cin >> id; person->set_id(id); cin .ignore(256 , '\n' ); cout << "Enter name: " ; getline(cin , *person->mutable_name()); cout << "Enter email address (blank for none): " ; string email; getline(cin , email); if (!email.empty()) { person->set_email(email); } 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[]) { GOOGLE_PROTOBUF_VERIFY_VERSION; if (argc != 2 ) { cerr << "Usage: " << argv[0 ] << " ADDRESS_BOOK_FILE" << endl ; return -1 ; } tutorial::AddressBook 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 ; } } 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_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 #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 ; } } } int main (int argc, char * argv[]) { GOOGLE_PROTOBUF_VERIFY_VERSION; if (argc != 2 ) { cerr << "Usage: " << argv[0 ] << " ADDRESS_BOOK_FILE" << endl ; return -1 ; } tutorial::AddressBook 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); 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_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. 运行效果