Google Protocol Buffers淺析(一)

本文主要偏向於介紹怎麼使用Google的Protocol Buffer技術來壓縮與解析你的數據文件,更加詳細的信息請參閱Google開放的開發者網頁文檔,地址爲:http://code.google.com/apis/protocolbuffers/docs/overview.html

一、簡單的介紹

當然,在繼續本文之前,讀者還是需要對Google Protocol Buffers有一些基本的認識。Protocol buffers是一個用來序列化結構化數據的技術,支持多種語言諸如C++、Java以及Python語言,可以使用該技術來持久化數據或者序列化成網絡傳輸的數據。相比較一些其他的XML技術而言,該技術的一個明顯特點就是更加節省空間(以二進制流存儲)、速度更快以及更加靈活。

通常,編寫一個protocol buffers應用需要經歷如下三步:

1、定義消息格式文件,最好以proto作爲後綴名

2、使用Google提供的protocol buffers編譯器來生成代碼文件,一般爲.h和.cc文件,主要是對消息格式以特定的語言方式描述

3、使用protocol buffers庫提供的API來編寫應用程序

二、定義Proto文件

proto文件即消息協議原型定義文件,在該文件中我們可以通過使用描述性語言,來良好的定義我們程序中需要用到數據格式。首先我們可以通過Google在線文檔上提供的一個電話簿的例子來了解下,不過稍微加了點改動。

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;

required bytes unsure = 5;
//Add byte array here
}

message AddressBook {
repeated Person person
= 1;
}

誠如你看到的一樣,消息格式定義很簡單,對於每個字段而言都有一個修飾符(required/repeated/optional)、字段類型(bool/string/bytes/int32等)和字段標籤(Tag)組成。

三個修飾符從詞義上可以很清楚的弄明白,

1)對於required的字段而言,初值是必須要提供的,否則字段的便是未初始化的。在Debug模式的buffer庫下編譯的話,序列化話的時候可能會失敗,而且在反序列化的時候對於該字段的解析會總是失敗的。所以,對於修飾符爲required的字段,請在序列化的時候務必給予初始化。

2)對於optional的字段而言,如果未進行初始化,那麼一個默認值將賦予該字段,當然也可以指定默認值,如上述proto定義中的PhoneType字段類型。

3)對於repeated的字段而言,該字段可以重複多個,google提供的這個addressbook例子便有個很好的該修飾符的應用場景,即每個人可能有多個電話號碼。在高級語言裏面,我們可以通過數組來實現,而在proto定義文件中可以使用repeated來修飾,從而達到相同目的。當然,出現0次也是包含在內的。

其中字段標籤標示了字段在二進制流中存放的位置,這個是必須的,而且序列化與反序列化的時候相同的字段的Tag值必須對應,否則反序列化會出現意想不到的問題。

三、編譯proto文件,生成特定語言數據的數據定義代碼

在定義好了proto文件,就可以將該文件作爲protocol buffers編譯器的輸入文件,編譯產生特定語言的數據定義代碼文件了。本文主要是針對C++語言,所以使用編譯器後生成的是.h與.cc的代碼文件。對於C++、Java還有Python都有各自的編譯器,下載地址:http://code.google.com/p/protobuf/downloads/list

當你下載完了對應的編譯器二進制文件後,就可以使用下列命令來完成編譯過程:

protoc.exe -proto_path=SRC --cpp_out=DST SRC/addressbook.proto

其中--proto_path指出proto文件所在的目錄,--cpp_out則是生成的代碼文件要放的目錄,最後的一個參數指出proto文件的路徑。如上述命令中可以看出,將SRC目錄下的addressbook.proto編譯後放在DST目錄下,應該會生成addressbook.pb.h和addressbook.pb.cc文件(/Files/royenhome/addressbook.rar)。

通過查看頭文件,可以發現針對每個字段都會大致生成如下幾種函數,以number爲例:

<!--

Code highlighting produced by Actipro CodeHighlighter (freeware)
http://www.CodeHighlighter.com/

--> // required string number = 1;
inline bool has_number() const;
inline
void clear_number();
inline
const ::std::string& number() const;
inline
void set_number(const ::std::string& value);
inline
void set_number(const char* value);
inline ::std::
string* mutable_number();

可以看出,對於每個字段會生成一個has函數(has_number)、clear清除函數(clear_number)、set函數(set_number)、get函數(number和mutable_number)。這兒解釋下get函數中的兩個函數的區別,對於原型爲const std::string &number() const的get函數而言,返回的是常量字段,不能對其值進行修改。但是在有一些情況下,對字段進行修改是必要的,所以提供了一個mutable版的get函數,通過獲取字段變量的指針,從而達到改變其值的目的。

而對於字段修飾符爲repeated的字段生成的函數,則稍微有一些不同,如phone字段,則編譯器會爲其產生如下的代碼:

<!--

Code highlighting produced by Actipro CodeHighlighter (freeware)
http://www.CodeHighlighter.com/

--> // repeated .Person.PhoneNumber phone = 4;
inline int phone_size() const;
inline
void clear_phone();
inline
const ::google::protobuf::RepeatedPtrField< ::Person_PhoneNumber >& phone() const;
inline ::google::protobuf::RepeatedPtrField
< ::Person_PhoneNumber >* mutable_phone();
inline
const ::Person_PhoneNumber& phone(int index) const;
inline ::Person_PhoneNumber
* mutable_phone(int index);
inline ::Person_PhoneNumber
* add_phone();

可以看出,set函數變成了add函數,這個其實很好理解。上面也說過,repeated修飾的字段在高級語言中的實現可能是個數組或動態數組,所以當然通過添加的方式來加入新的字段值。而起get函數也變化很大,這個也不用多說了。

好了,本文主要是對了解protocol buffer作了些簡單的介紹,當然更詳細的還是看官方文檔。下篇文章開始將介紹怎麼利用protocol buffers來完成序列化與反序列化數據。

歡迎轉載,轉載時請務必保留原文出處:http://www.cnblogs.com/royenhome ,謝謝合作!

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章