Protocol Buffers

Table of Contents

1. Protocol Buffers 简介

Protocol Buffers is a method of serializing structured data. The design goals for Protocol Buffers emphasized simplicity and performance. In particular, it was designed to be smaller and faster than XML.

Protocol Buffers are similar to the Apache Thrift (used by Facebook).

1.1. 从源码安装 Protocol Buffers

下面是从源码安装 Protocol Buffers 的基本步骤:

$ ./configure
$ make
$ make check
$ sudo make install      # 默认安装在/usr/local/中

参考:https://github.com/google/protobuf/blob/master/src/README.md

2. Protocol Buffers 简单例子

2.1. Defining Your Protocol Format

假设你要开发一个“通讯簿”应用程序,你可以定义如下消息格式:

syntax = "proto3";

package tutorial;

message Person {
  string name = 1;     // 每个字段都有一个唯一的数字标签,其作用是在二进制message中唯一标识该字段
                       // 新软件中往proto中增加字段时,为保证兼容性请不要改变已有字段中的数字标签
  int32 id = 2;
  string email = 3;

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

  message PhoneNumber {     // 这是嵌套的message
    string number = 1;
    PhoneType type = 2;
  }

  repeated PhoneNumber phone = 4;  // repeated指定该域可重复“零次或多次”,相当于不定大小的数组
}

2.2. Compiling Your Protocol Buffers

后面测试中,将使用 C++测试应用程序。所以这里生成 C++相关的辅助类。

$ protoc -I=./ --cpp_out=./ ./addressbook.proto          # 把--cpp_out换为--java_out等可以生成其它语言的辅助类。

上面命令成功执行后,会生成下面两个文件:
(1)addressbook.pb.h: 辅助类头文件。
(2)addressbook.pb.cc: 上面头文件的实现。

如果打开 addressbook.pb.h,可以发现它提供了下面一些辅助函数:

// part of file addressbook.pb.h

// name
inline bool has_name() const;
inline void clear_name();
inline const ::std::string& name() const;
inline void set_name(const ::std::string& value);
inline void set_name(const char* value);
inline ::std::string* mutable_name();

// id
inline bool has_id() const;
inline void clear_id();
inline int32_t id() const;
inline void set_id(int32_t value);

// email
inline bool has_email() const;
inline void clear_email();
inline const ::std::string& email() const;
inline void set_email(const ::std::string& value);
inline void set_email(const char* value);
inline ::std::string* mutable_email();

// phones
inline int phone_size() const;
inline void clear_phone();
inline const ::google::protobuf::RepeatedPtrField< ::tutorial::Person_PhoneNumber >& phone() const;
inline ::google::protobuf::RepeatedPtrField< ::tutorial::Person_PhoneNumber >* mutable_phone();
inline const ::tutorial::Person_PhoneNumber& phone(int index) const;
inline ::tutorial::Person_PhoneNumber* mutable_phone(int index);
inline ::tutorial::Person_PhoneNumber* add_phone();

2.3. C++测试程序

下面是一个简单的程序,首先把数据序列化到文件 myfile 中,然后再读取它。

// file test.cpp
#include <iostream>
#include <fstream>
#include <string>
#include "addressbook.pb.h"                   // 这是protoc生成的头文件

using namespace std;

void writeAddressToFile() {
    tutorial::Person person;
    person.set_name("John Doe");
    person.set_id(1234);
    person.set_email("jdoe@example.com");

    tutorial::Person::PhoneNumber* phone_number1 = person.add_phone();
    phone_number1->set_number("+861381234XXXX");
    phone_number1->set_type(tutorial::Person::MOBILE);

    tutorial::Person::PhoneNumber* phone_number2 = person.add_phone();
    phone_number2->set_number("010XXXX");
    phone_number2->set_type(tutorial::Person::HOME);

    fstream output("myfile", ios::out | ios::binary);
    person.SerializeToOstream(&output);
}

void readAddressFromFile() {
    fstream input("myfile", ios::in | ios::binary);
    tutorial::Person person;
    person.ParseFromIstream(&input);
    cout << "Name: " << person.name() << endl;
    cout << "Id: " << person.id() << endl;
    cout << "E-mail: " << person.email() << endl;

    for (int j = 0; j < person.phone_size(); j++) {
        const tutorial::Person::PhoneNumber& phone_number = person.phone(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;
        }
        cout << phone_number.number() << endl;
    }
}

int main() {
    writeAddressToFile();
    readAddressFromFile();
    return 0;
}

对上面程序进行编译测试,如下:

$ g++ -o test test.cpp addressbook.pb.cc -lprotobuf
$ ./test
Name: John Doe
Id: 1234
E-mail: jdoe@example.com
Mobile phone #: +861381234XXXX
Home phone #: 010XXXX

3. Protocol Buffers 语言规范

Author: cig01

Created: <2017-07-30 Sun>

Last updated: <2018-04-19 Thu>

Creator: Emacs 27.1 (Org mode 9.4)