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值写入到之前开辟的内存中。
 

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