protobuf簡介及使用流程
1. Protobuf是什么
ProtoBuf(全稱Protocol Buffer)是數(shù)據(jù)結構序列化和反序列化框架,它具有以下特點:
- 語??關、平臺無關:即 ProtoBuf ?持 Java、C++、Python 等多種語?,?持多個平臺
- ?效:即? XML 更小、更快、更為簡單
- 擴展性、兼容性好:你可以更新數(shù)據(jù)結構,而不影響和破壞原有的舊程序
2. Protobuf使?流程介紹
- 編寫 .proto ?件,?的是為了定義結構對象(message)及屬性內容。
- 使? protoc 編譯器編譯 .proto ?件,?成?系列接?代碼,存放在新?成頭?件和源?件中。
- 依賴?成的接?,將編譯?成的頭?件包含進我們的代碼中,實現(xiàn)對 .proto ?件中定義的字段進行設置和獲取,和對 message 對象進行序列化和反序列化
3. ProtoBuf快速上手
我們以?個簡單通訊錄的實現(xiàn)來驅動對Protobuf的學習。在通訊錄demo中,我們將實現(xiàn):
- 對?個聯(lián)系?的信息使? Protobuf 進?序列化,并將結果打印出來。
- 對序列化后的內容使? Protobuf 進?反序列,解析出聯(lián)系?信息并打印出來。
- 聯(lián)系?包含以下信息: 姓名、年齡。
通過通訊錄demo,我們能快速的了解ProtoBuf的使?流程。
3.1 創(chuàng)建 .proto ?件
- 創(chuàng)建 .proto ?件時,?件命名應該使?全?寫字?命名,多個字?之間? _ 連接。 例如:lower_snake_case.proto
- 書寫 .proto ?件代碼時,應使? 2 個空格的縮進。
我們?yōu)橥ㄓ嶄?demo 新建?件: contacts.proto
3.2 添加注釋
向?件添加注釋,可使? // 或者 /* … */
3.3 具體編寫
指定 proto3 語法:
Protocol Buffers 語?版本3,簡稱 proto3,是 .proto ?件最新的語法版本。proto3 簡化了 ProtocolBuffers 語?,既易于使?,?可以在更?泛的編程語?中使?。它允許你使? Java,C++,Python等多種語??成 protocol buffer 代碼。在 .proto ?件中,要使? syntax = “proto3”; 來指定?件語法為 proto3,并且必須寫在除去注釋內容的第??。 如果沒有指定,編譯器會使?proto2語法。
在通訊錄 demo 的 contacts.proto ?件中,可以為?件指定 proto3 語法,內容如下:
syntax = "proto3";
package 聲明符:
package 是?個可選的聲明符,能表? .proto ?件的命名空間,在項?中要有唯?性。它的作?是為了避免我們定義的消息出現(xiàn)沖突。
在通訊錄 demo 的 contacts.proto ?件中,可以聲明其命名空間,內容如下:
package contacts;
定義消息(message):
消息(message): 要定義的結構化對象,我們可以給這個結構化對象中定義其對應的屬性內容。在網(wǎng)絡傳輸中,我們需要為傳輸雙?定制協(xié)議。定制協(xié)議說?了就是定義結構體或者結構化數(shù)據(jù),?如,tcp,udp 報?就是結構化的。再?如將數(shù)據(jù)持久化存儲到數(shù)據(jù)庫時,會將?系列元數(shù)據(jù)統(tǒng)??對象組織起來,再進?存儲。ProtoBuf 就是以 message 的方式來?持我們定制協(xié)議字段,后期幫助我們形成類和?法來使用。
在通訊錄 demo 中我們就需要為聯(lián)系人定義?個 message:
.proto ?件中定義?個消息類型的格式為:
message 消息類型名{ } 消息類型命名規(guī)范:使?駝峰命名法,?字??寫。
為 contacts.proto(通訊錄 demo)新增聯(lián)系?message
syntax = "proto3"; package contacts; // 定義聯(lián)系?消息 message PeopleInfo { }
定義消息字段:
在 message 中我們可以定義其屬性字段,字段定義格式為:字段類型 字段名 = 字段唯?編號;
- 字段名稱命名規(guī)范:全?寫字?,多個字?之間? _ 連接。
- 字段類型分為:標量數(shù)據(jù)類型 和 特殊類型(包括枚舉、其他消息類型等)。
- 字段唯?編號:?來標識字段,?旦開始使?就不能夠再改變。
樣例:
// 聲明語法版本 syntax = "proto3"; // 聲明代碼的命名空間 package contacts; //結構化對象的描述 message PeopleInfo{ // 各個字段描述: 字段類型 字段名 = 字段唯一編號 string name = 1; int32 age = 2; }
注:這?還要特別講解?下字段唯?編號的范圍:1 ~ 536,870,911 (2^29 - 1) ,其中 19000 ~ 19999 不可?。
19000 ~ 19999 不可?是因為:在 Protobuf 協(xié)議的實現(xiàn)中,對這些數(shù)進?了預留。如果?要在.proto?件中使?這些預留標識號,例如將 name 字段的編號設置為19000,編譯時就會報警。
值得?提的是,范圍為 1 ~ 15 的字段編號需要?個字節(jié)進?編碼, 16 ~ 2047 內的數(shù)字需要兩個字節(jié)進?編碼。編碼后的字節(jié)不僅只包含了編號,還包含了字段類型。所以 1 ~ 15 要?來標記出現(xiàn)?常頻繁的字段,要為將來有可能添加的、頻繁出現(xiàn)的字段預留?些出來。
3.4 編譯 contacts.proto 文件
編譯命令?格式為:
protoc [--proto_path=IMPORT_PATH] --cpp_out=DST_DIR path/to/file.proto protoc 是 Protocol Buffer 提供的命令?編譯?具。 --proto_path 指定被編譯的.proto?件所在?錄,可多次指定??珊唽懗?-I IMPORT_PATH 。如不指 定該參數(shù),則在當前?錄進?搜索。當某個.proto ?件 import 其他.proto ?件時, 或需要編譯的 .proto ?件不在當前?錄下,這時就要?-I來指定搜索?錄。 --cpp_out= 指編譯后的?件為 C++ ?件。 OUT_DIR 編譯后?成?件的?標路徑。 path/to/file.proto 要編譯的.proto?件
編譯 contacts.proto ?件命令如下:
protoc --cpp_out=. contacts.proto
編譯 contacts.proto ?件后,會?成所選擇語?的代碼,我們選擇的是C++,所以編譯后?成了兩個文件: contacts.pb.h contacts.pb.cc 。
對于編譯?成的 C++ 代碼,包含了以下內容 :
- 對于每個 message ,都會?成?個對應的消息類
- 在消息類中,編譯器為每個字段提供了獲取和設置?法,以及?下其他能夠操作字段的方法
- 編輯器會針對于每個 .proto ?件?成 .h 和 .cc ?件,分別?來存放類的聲明與類的實現(xiàn)
contacts.pb.h 部分代碼展示:
class PeopleInfo final : public ::PROTOBUF_NAMESPACE_ID::Message { public: using ::PROTOBUF_NAMESPACE_ID::Message::CopyFrom; void CopyFrom(const PeopleInfo& from); using ::PROTOBUF_NAMESPACE_ID::Message::MergeFrom; void MergeFrom( const PeopleInfo& from) { PeopleInfo::MergeImpl(*this, from); } static ::PROTOBUF_NAMESPACE_ID::StringPiece FullMessageName() { return "PeopleInfo"; } // string name = 1; void clear_name(); const std::string& name() const; template <typename ArgT0 = const std::string&, typename... ArgT> void set_name(ArgT0&& arg0, ArgT... args); std::string* mutable_name(); PROTOBUF_NODISCARD std::string* release_name(); void set_allocated_name(std::string* name); // int32 age = 2; void clear_age(); int32_t age() const; void set_age(int32_t value); };
上述的例?中:
- 每個字段都有設置和獲取的方法, getter 的名稱與?寫字段完全相同,setter ?法以 set_ 開頭。
- 每個字段都有?個 clear_ 方法,可以將字段重新設置回 empty 狀態(tài)。
contacts.pb.cc 中的代碼就是對類聲明?法的?些實現(xiàn),在這?就不展開了。
到這?有人可能就有疑惑了,那之前提到的序列化和反序列化?法在哪?呢?在消息類的?類MessageLite 中,提供了讀寫消息實例的方法,包括序列化?法和反序列化?法。
class MessageLite { public: //序列化: bool SerializeToOstream(ostream* output) const; // 將序列化后數(shù)據(jù)寫??件 流 bool SerializeToArray(void *data, int size) const; bool SerializeToString(string* output) const; //反序列化: bool ParseFromIstream(istream* input); // 從流中讀取數(shù)據(jù),再進?反序列化 動作 bool ParseFromArray(const void* data, int size); bool ParseFromString(const string& data); };
注意:
- 序列化的結果為?進制字節(jié)序列,???本格式。
- 以上三種序列化的?法沒有本質上的區(qū)別,只是序列化后輸出的格式不同,可以供不同的應?場景使?。
- 序列化的 API 函數(shù)均為const成員函數(shù),因為序列化不會改變類對象的內容, ?是將序列化的結果保存到函數(shù)?參指定的地址中
序列化與反序列化的使用:
創(chuàng)建?個測試?件 test.cc,?法中我們實現(xiàn):
- 對?個聯(lián)系?的信息使? PB 進?序列化,并將序列化結果打印出來。
- 對序列化后的內容使? PB 進?反序列,解析出聯(lián)系?信息并打印出來。
#include <iostream> #include "contacts.pb.h" using namespace std; int main() { string people_str; { contacts::PeopleInfo people; people.set_age(20); people.set_name("忘憂"); if(!people.SerializeToString(&people_str)) { cout << "序列化聯(lián)系人失敗" <<endl; } cout << "序列化之后的 people_str: " << people_str << endl; // 反序列化 { contacts::PeopleInfo people; if(!people.ParseFromString(people_str)) { cout << "反序列化聯(lián)系人失敗" <<endl; } cout << "Parse age: " << people.age() << endl; cout << "Parse name: " << people.name() << endl; } } return 0; }
代碼書寫完成后,編譯 test.cc,生成可執(zhí)行程序:
g++ test.cc contacts.pb.cc -o test -std=c++11 -lprotobuf
執(zhí)?可執(zhí)?程序,可以看? people 經(jīng)過序列化和反序列化后的結果:
由于 ProtoBuf 是把聯(lián)系?對象序列化成了?進制序列,這?? string 來作為接收?進制序列的容器。所以在終端打印的時候會有換?等?些亂碼顯?。另外相對于 xml 和 JSON 來說,因為PB被編碼成?進制,破解成本增?,ProtoBuf 編碼是相對安全的。
到此這篇關于protobuf簡介及使用流程的文章就介紹到這了,更多相關protobuf使用內容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!
相關文章
SpringBoot整合ELK實現(xiàn)日志監(jiān)控
這篇文章主要為大家詳細介紹了SpringBoot整合ELK實現(xiàn)日志監(jiān)控的相關知識,文中的示例代碼講解詳細,感興趣的小伙伴可以跟隨小編一起學習一下2024-11-11詳解mybatis中association和collection的column傳入多個參數(shù)問題
這篇文章主要介紹了詳解mybatis中association和collection的column傳入多個參數(shù)問題,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧2019-10-10SpringBoot中的異常處理與參數(shù)校驗的方法實現(xiàn)
這篇文章主要介紹了SpringBoot中的異常處理與參數(shù)校驗的方法實現(xiàn),文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧2020-04-04Spring boot按日切分spring boot的nohup.out日志文件的方法
過大的日志文件維護起來存在諸多問題,所以最好是能夠按日或按大小切分日志文件,下面小編給大家?guī)砹薙pring boot按日切分spring boot的nohup.out日志文件的方法,一起看看吧2018-08-08Java8新特性Stream流中anyMatch和allMatch和noneMatch的區(qū)別解析
這篇文章主要介紹了Java8新特性Stream流中anyMatch和allMatch和noneMatch的區(qū)別解析,本文給大家介紹的非常詳細,對大家的學習或工作具有一定的參考借鑒價值,需要的朋友參考下吧2024-01-01