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值寫入到之前開闢的內存中。