protobuf 相關知識 C++

 protobuf是一種序列化方法.

1. message字段包括以下幾種情況

(1)singular:包涵該字段一次或則零次

(2)repeated:可以重複任意多次

2.可以在.proto文件中定義多種message類型。但是當單個文件定義大量不同依賴關係的messages時,會導致依賴性膨脹。

建議每個.proto文件包含儘可能少的message類型。

3.對於C++,編譯器從每個.proto生成一個.h和.cc文件,其中包含文件中描述的每種message類型對應的類。

4.保留值:如果通過刪除枚舉條目或則註釋掉來更新枚舉類型,未來的用戶可能對message做出自己的修改或者重複使用這些值。如果以後加載相同的.proto的舊版本,可能導致數據損壞和隱藏錯誤。

5.如果希望message格式具有額外的字段,但仍然希望使用舊格式創建代碼。

(1)不要更改現有字段和字段編號

(2)添加的新字段都應該是optional或則repeated

(3)在更新的message類型中不再使用字段編號,就可以刪除非必填字段。可以將字段編號保留(Reserved),以便將來你的.proto的用戶不會不小心重用了這個編號。

(4)optional和repeated兼容

5.擴展Extensions

通過擴展,可以聲明message中的一些列字段編號用於第三方擴展。擴展名是那些未由原始.proto文件定義的字段的佔位符。

6.Oneof

如果你的message包含許多可選字段,並且最多隻能同時設置其中一個字段,則可以使用Oneof功能強制執行此行爲並節省內存。

7.Packages

可以optional可選的包說明添加到.proto文件,防止protocol message類型之間的名稱衝突。

package foo.bar;
message open{ ...}

可以在定義message類型的字段時使用包說明符

  message Foo{
     requird foo.bar.Open open=1;
  }

8.定位服務

如果將messag類型與RPC(遠程過程調用)系統一起使用,則可以在.proto文件中定義RPC服務接口,protocol buffer編譯器將使用你選擇的語言生成服務接口代碼和存根。如果定義了一個RPC服務,其中一個獲取SearchRequest並返回SearchResponse的方法,可以在.proto中定義。

service SearchService{
   rpc Search(SearchRequest) returns (SearchResponse);
}

protocol編譯器將生成一個名爲SearchService的抽象接口和相應的“存根”實現。RpcChannel是一個抽象接口,必須根據自己的RPC系統自行定義。如果實現一個RpcChannel,它將message序列化並通過HTTP將其發送到服務器。

using google::protobuf;

 protobuf::RpcChannel* channel;
 protobuf::RpcController* controller;
 SearchService* service;
 SearchRequest request;
 SearchResponse response;

 void DoSearch(){
     channel=new MyRpcChannel("somehost.example.com:1234");
     controller=new MyRpcController;

     service=new SearchService::Stub(channel);
     request.set_query("protocol buffers");

     service->Search(controller,request,response,protobuf::NewCallback(&Done));
 }
 void Done()
 {
     delete service;
     delete channel;
     delete controller;
 }

在服務端,可以實現註冊服務的RPC服務器。

 using google::protobuf;
 class ExampleSearchService:public SearchService{
     public:
     void Search(protobuf::RpcController* controller,
                 const SearchRequest* request,
                 SearchResponse* response,
                 protobuf::Closure* done){
                     if(request->query()=="google"){
                         response->add_result()->set_url("http://www.google.com");
                     }else if(request->query()=="protocol buffers"){
                         response->add_result()->set_url("http:...");
                     }
                     done->Run();
                 }
 };

 int main()
 {
     MyRpcServer server;
     protobuf::Service* service=new ExampleSearchService;
     service.ExportOnPort(1234,service);
     server.Run();
     delete service;
     return 0;
 }

9.使用其它消息類型

消息類型可以作爲字段類型,當我們需要在SearchReponse消息中包含Result消息,可以將Result消息類型的定義放在同一個.proto文件中同時在SearchResponse消息中指定一個Result類型字段。

10.Map映射表

如果需要創建關聯映射表作爲定義的數據的一部分,protocol buffers提供了快捷的語法。

 map<key_type,value_type>map_field =N;

11.Message和字段命名

使用駝峯命名法命名message,如:ServerRequest

使用下劃線命名字段,如:shen_name

 message ServerRequest{
     required string shen_name=1;
 }

生存的訪問器如下所示:

 const string & shen_name(){...}
 void set_shen_name(const string& x){...}

12 枚舉Enums

使用駝峯命名法命名枚舉類型,使用"大寫_下劃線_大寫"的方式命名枚舉值。

enum Foo{
     FIRST_VALUE=0;
     SECOND_VALUE=1;
 }

13多消息(message)流

如果將多條消息(Message)寫入單個文件或流,則需要跟蹤一條消息的結束位置和開始位置。解決的方法是在每條消息本身內容之前記錄消息的大小或者長度。當重新讀取消息時,可讀取其大小,讀取對應字節到單獨的緩衝區,並解析消息內容。

 

1.TLV格式:Tag-length-Value,Tag作爲該字段的唯一標識,Length代表Value數據域的長度,
  最後的Value是數據本身。
2.每個message由一個個字段組成,每個字段可以劃分爲:Tag-[length]-Value。
3.Tag採用Varints編碼方案進行編碼.針對sint32、sin64採用sint64編碼。
   field_number:message定義字段時指定的字段編碼。
   wire_type:Protobuf編碼類型,根據這個類型可以選擇不同的Value編碼方案。
4.每個field根據類型可以有2種格式:
   Tag-Length-Value:Length-delimited編碼類型使用這幾個結構
   Tag-Value:編碼類型表中Varint、64-bit、32-bit使用這種結構。
5.Varints編碼規則:
  (1)在每個字節開頭的bit設置了msb,標識是否需要繼續讀取下一個字節。
  (2)存儲數字對應的二進制補碼。
  (3)補碼的低位排在前面。
   通過Varints可以讓小的數字用更少的字節表示,從而提高了空間利用率。
6.protobuf序列化使用過程:
  (1)定義.proto文件
  (2)protoc編譯器編譯.proto文件生成一系列接口代碼
  (3)調用生成的接口實現對.proto文件的字段的讀取以及message對象的序列化、反序列化方法。
7.protobuf序列化的幾個關鍵函數:
   ByteSizeLong:計算對象序列化所需要的空間大小,在內存中開闢相應大小的空間。
   WriteTagToArray:將Tag值寫入到之前開闢的內存中
   WriteStringWithSizeToArray:將Length+Value值寫入到之前開闢的內存中。
 

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