GoogleProtocol Buffers 简称 Protobuf,是 Google 公司内部的混合语言数据标准.它提供一种轻量,高效的结构化数据存储结构.
简介
为什么要使用protobuf?
1. 官方文档中提到一些protobuf的优点, protobuf灵活高效的结构化数据存储格式.方便用于序列化, 适合做RPC的数据交换.
2. 相比 XML , protobuf 比 XML 更小、更快、更简单.仅需要写一个 *.proto 文件描述需要的数据结构, protobuf会帮助你实现相关类和方法(自动化多好!).
3. 目前提供 C++, Java, Python, Go, C#等多种语言 的API
安装
在ubuntu上一键安装
sudoapt-get install protobuf-compiler
定义proto文件
定义proto文件较为简单,给每个结构体数据定义一个message,然后给结构体中的每个数据添加一个类型和名称,例如,想要定义一个address.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;
}
有如你所见,消息格式很简单,每个消息类型拥有一个或多个特定的数字字段,每个字段拥有一个名字和一个值类型。值类型可以是数字(整数或浮点)、布尔型、字符串、原始字节或者其他ProtocolBuffer类型,还允许数据结构的分级。你可以指定可选字段,必选字段和重复字段。
一旦你定义了自己的报文格式(message),你就可以运行ProtocolBuffer编译器,将你的 .proto 文件编译成特定语言的类。这些类提供了简单的方法访问每个字段(像是 query()和 set_query() ),像是访问类的方法一样将结构串行化或反串行化。
编译proto文件
现在已经拥有了 .proto文件,下一步就是编译生成相关的访问类。运行编译器 protoc 编译你的 .proto文件。
1. 如果还没安装编译器则下载并按照README的安装。
2. 运行编译器,指定源目录和目标目录,定位你的 .proto文件到源目录,然后执行:
protoc-I=$SRC_DIR –cpp_out=$DST_DIRaddressbook.proto
此操作生成c++格式的proto文件,如果想生成Python、java等类型的,把cpp换成python、java等即可,该操作生成如下两个文件:
addressbook.pb.h 定义了c++的头文件
addressbook.pb.cc 包含了类的实现
protocol buffer API
编译后的name、id、emal、和phone有如下方法:
// 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();
如你所见,获取字段的方法都是字段名称的小写,如
inline const ::std::string& name()const;
inline int32_t id()const;
inline const ::std::string& email()const;
inline const ::tutorial::Person_PhoneNumber& phone(int index) const;
设置字段名称的方法都已set_开头,singular (required or optional) 类型的字段有has_开头的方法,如果该字段已经设置了,那么该方法返回true
最后,每个字段都有一个clear_方法来把字段复位为空状态
数字类型的字段id只有基本的设置字段的方法,string型的字段name和email有几个其他的方法,mutable_开头的方法让你得到一个直接指向string类型的指针,及时email未被设置,你也可以调用mutable_email(),它将会自动初始化为一个空的string。如果字段在调用这个方法之前没有被设置,那么返回的字符串将为空(而不是缺省值)。调用这个方法后,has_foo()返回 true,foo()返回被写入到字符串(foo)中的任何值。通过调用 Clear()或 clear_foo()清除此指针。
Repeated类型的字段有一些特殊的方法
_size:检查有多少个字段
使用index来获取特定的字段
更新一个已经存在的字段
add_方法添加一个新的字段
生成的代码包含一个PthonType枚举,可以通过Person::PthonType或者它们的值Person::MOBILE, Person::HOME,和Person::WORK来调用它们.
标准消息方法
每个消息还包括一些其他方法用来检查和操作整个消息
bool IsInitialized() const; //检查是否所有的requied类型的消息都被设置了
string DebugString() const;//返回消息的一个可读类型,debug时很有用
void CopyFrom(const Person& from);//用给定的值来覆盖消息
void Clear();//把所有元素置回空状态
解析和序列化
最终,protocol buffer类有使用二进制格式来读写消息的方法:
bool SerializeToString(string* output) const;//序列化消息并把字节存储在给定的string类型中,注意字节是二进制类型,而不是text类型,我们只是使用string类型作为一个转换容易
bool ParseFromString(const string& data);//从string中解析消息
bool SerializeToOstream(ostream* output)const;//把消息写入一个给定的c++ostream中
bool ParseFromIstream(istream* input);//从一个给定的c++ istream中解析消息