凌云的博客

行胜于言

Protocol Buffers 入门

分类:other| 发布时间:2014-05-11 22:10:42


简介

Protocol Buffers是一个灵活的,高效的,用于序列化结构化数据的自动机制- 有点类似XML,但更小巧,快捷和简单。

安装

到项目的主页下载源代码: protobuf

然后键入以下命令进行安装(Linux环境):

$cd protobuf
$./configure
$make
$sudo make install

例子

在源代码的exmaples子目录下有一个使用Protocol Buffers的例子,通过键入以下命令 编译此例子:

$make

编译完成后会生成C++、Java和Python版本的例子,本文针对C++版本进行讲解。

运行例子

编译完成后会在example目录下生成add_person_cpp和list_person_cpp,让我们来 运行下这两个例子:

$./add_person_cpp test.file
test.file: File not found.  Creating a new file.
Enter person ID number: 1
Enter name: zhangsan
Enter email address (blank for none): zhangsan@gmail.com
Enter a phone number (or leave blank to finish): 110
Is this a mobile, home, or work phone? home
Enter a phone number (or leave blank to finish): 
$./list_people_cpp test.file
Person ID: 1
  Name: zhangsan
  E-mail address: zhangsan@gmail.com
  Home phone #: 110

毫无疑问,可以正确地进行读写。

.proto文件

在这个例子中有一个addressbook.proto的文件, 这个文件是用于定义协议的格式的,内容如下:

package tutorial;

message Person {
    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 = 1;
}

.proto文件由一个package声明语句开始,在C++中表示这个文件的内容都放在 namespace tutorial下。

接下来你会看到message的定义,message的字段可以是基础类型也可以是其他的message, 其中基础类型有:bool, int32, float, double, 和string。当然你也可以定义一系列的 enum值。

后面的"=1"、"=2"表示在二进制编码的过程中用于给每个字段打标签的唯一值。1-15的值 会比其他值少需要1个字节,所以尽量将1-15分配给常用的字段。

每个字段都由以下关键字修饰:

  • required 表示这个字段必须提供,否则将认为message未初始化。 在Debug模式下这种情况会导致断言失败。
  • optional 这个字段可以提供也可以不提供,如果不提供它的值将被设为默认值 (数字类型为0,字符串为空字符串)。
  • repeated 这个字段可以重复任意次(包括0),可以认为是动态数组。

关于.proto文件的详细格式请查看: Protocol Buffer Language Guide

Protocol Buffer接口

在example目录下,可以使用以下命令编译.proto文件

protoc -I=./--cpp_out=./ ./addressbook.proto

这条命令会生成addressbook.pb.h和addressbook.pb.cc文件。

通过查看生成的头文件会发现生成了以下接口:

// 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();

// phone
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();

可以看到生成的接口中,getter就是和字段名相同的函数,除此外还有has_,clear_,set_等函数, 而对于string类型的字段还会多出一个mutable_的函数。 而对于repeated修饰的字段在这之外还会有一些额外的函数,比如_size等。

由于所有的message类型都是从class Message继承下来的, 因此除了以上生成的接口外还继承了class Message的所有接口: complete API documentation for Message