Protocol Buffer技术理解

来源:互联网 发布:淘宝618大促报名 编辑:程序博客网 时间:2024/06/05 06:40

 各位亲 有时间可以去看看我的  “金骏家居淘宝店” http://jinjun1688.taobao.com/shop/view_shop.htm?tracelog=twddp 买时说明在我的博客看到有优惠哦 还有意外礼品赠送  真正的程序员淘宝店

Protocol  Buffer技术理解

protocol buffer :一种跨平台,支持多种语言,扩展性好的用于通讯协议,数据存储的的结构化数据串行化方法。

一在mac上安装protocol buffer的基本步骤

1)    下载相应文件并解压到本地文件夹

2)    确定电脑是否安装gcc 若没有则独立安装:Xcode->Preferences->Downloads下的Components下,安装command Line Tools。

3)    切换到管理员身份

4)    在终端下进入到protobuf文件夹。

5)    在终端下依次输入:

./configure

make

make check

make install

二、定义第一个Protocol Buffer消息

1.在新建MyMessage.proto文件中定义的一组Protocol Buffer格式的消息编译成目标语言(C++)的代码。

     如:option optimize_for = LITE_RUNTIME;

    message LogonReqMessage {
          required int64 acctID = 1;
          required string passwd = 2;
     }

由于我们在MyMessage文件中定义选项optimize_for的值为LITE_RUNTIME,因此由该.proto文件生成的所有C++类的父类均为::google::protobuf::MessageLite,而非::google::protobuf::Message

1. message是消息定义的关键字,等同于C++中的struct/class,或是Java中的class。
       2. LogonReqMessage为消息的名字,等同于结构体名或类名。
       3.required前缀表示该字段为必要字段,既在序列化和反序列化之前该字段必须已经被赋值。

4.optional和repeated,带有这两种限定符的消息字段则没有required字段这样的限制。相比于optional,repeated主要用于表示数组字段。

required 表示这个域是必需的
    optional
该域选,出现0次或1次
    repeated 重复出现,0次或多次

注:有optional说明的域可以有一个默认值,在不指定该域时使用  如optional PhoneTypetype = 2 [default = HOME];

        5.string是域的类型,可是简单的标量类型(如bool,int32,float,double,string等),也可是复合类型(message,enum等)

6.  name是域的名字,=1是给域一个数字标签,这会影响到该域在二进制文件中顺序。
关于这个数字标签也是有说明的,1到15是只使用一个字节编号,而其他的使用多个字节,所以应把1-15编号给最经常使用的域。数字标签的最大值为2**29 - 1,其中还有一段是保留用于proto的实现,从19000到19999

7.可以访问一个嵌套定义在另一个message类型中的message,但需使用域范围标示,如同的c++里使用另一个命名空间的类:person::PhoneNumber

8.proto支持包的使用,以防止命名冲突。在文件的开始部分指定:package tutorial

 三、定义第二个(含有枚举字段)Protocol Buffer消息。 在定义Protocol Buffer的消息时,可以使用和C++/Java代码同样的方式添加注释。  

enum UserStatus{     

 OFFLINE = 0;  

 
ONLINE = 1;  

 
 }  

messageUserInfo {  

 required int64 acctID = 1;        

 required string name = 2;    

 required UserStatus status = 3;  

}
     

这里将给出以上消息定义的关键性说明(仅包括上一小节中没有描述的)。
    

 1. enum是枚举类型定义的关键字,等同于C++/Java中的enum。
     

   2.UserStatus为枚举的名字。
 

 3. 和C++/Java中的枚举不同的是,枚举值之间的分隔符是分号,而不是逗号。
  

4.OFFLINE/ONLINE为枚举值。
  

5.0和1表示枚举值所对应的实际整型值,和C/C++一样,可以为枚举值指定任意整型值,而无需总是从0开始定义。如:
    

enumOperationCode {  

    LOGON_REQ_CODE = 101;

   LOGOUT_REQ_CODE = 102;

    RETRIEVE_BUDDIES_REQ_CODE = 103;

   LOGON_RESP_CODE = 1001;

    LOGOUT_RESP_CODE = 1002;

   RETRIEVE_BUDDIES_RESP_CODE = 1003;

     }    

  四、定义第三个(含有嵌套消息字段)Protocol Buffer消息。
    

  我们可以在同一个.proto文件中定义多个message,这样便可以很容易的实现嵌套消息的定义。如:
    

 enum UserStatus { 

  OFFLINE = 0; 

  ONLINE = 1;     

 }     

messageUserInfo {     

  required int64 acctID = 1;       

  required string name = 2;       

  required UserStatus status = 3;   

  }  

messageLogonRespMessage {     

  required LoginResult logonResult = 1;   

  required UserInfo userInfo = 2;  

  }
    

 这里将给出以上消息定义的关键性说明(仅包括上两小节中没有描述的)。
  

1.  LogonRespMessage消息的定义中包含另外一个消息类型作为其字段,如UserInfo userInfo。
  

  2. 上例中的UserInfo和LogonRespMessage被定义在同一个.proto文件中,那么我们是否可以包含在其他.proto文件中定义的message呢?Protocol Buffer提供了另外一个关键字import,这样我们便可以将很多通用的message定义在同一个.proto文件中,而其他消息定义文件可以通过import的方式将该文件中定义的消息包含进来,如:
import "myproject/CommonMessages.proto"

命令行编译工具

protoc --proto_path=IMPORT_PATH -cpp_out=DST_DIR --java_out=DST_DIR --python_out=DST_DIRpath/to/file.proto
     

这里将给出上述命令的参数解释。
   

1. protoc为Protocol Buffer提供的命令行编译工具。


  2.--proto_path等同于-I选项,主要用于指定待编译的.proto消息定义文件所在的目录,该选项可以被同时指定多个。
    

 3. --cpp_out选项表示生成C++代码,--java_out表示生成Java代码,--python_out则表示生成Python代码,其后的目录为生成后的代码所存放的目录。
     

4.path/to/file.proto表示待编译的消息定义文件。


注:对于C++而言,通过Protocol Buffer编译工具,可以将每个.proto文件生成出一对.h和.cc的C++代码文件。生成后的文件可以直接加载到应用程序所在的工程项目中。如:MyMessage.proto生成的文件为MyMessage.pb.h和MyMessage.pb.cc。

扩展一个protocol buffer:想要实现新buffer向后兼容,并且旧的buffer能够向前兼容,那么你在新的protocol buffer中就要遵守其他的一些规则了:

1) 对已存在的任何字段,你都不能更改其标识(tag)号。

2)你绝对不能添加或删除任何required的字段。

3)你可以添加新的optional或repeated的字段,但是你必须使用新的标识(tag)号(例如,在这个protocol buffer中从未使用过的标识号——甚至于已经被删除过的字段使用过的标识号也不行)。

每个字段中的函数:

 required stringnumber = 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函数中的两个函数的区别:

conststd::string &number() const;// 返回的是常量字段,不能对其值进行修改。

  inline ::std::string*mutable_number(); //对字段进行修改,通过获取字段变量的指针,改变其值的目的。

ProtoBuf编码基础——Varints:varints是一种将一个整数序列化为一个或者多个Bytes的方法,越小的整数,使用的Bytes越少。

Varints的基本规则是:

(a) 每个Byte的最高位(msb)是标志位,如果该位为1,表示该Byte后面还有其它Byte,如果该位为0,表示该Byte是最后一个Byte。

(b)每个Byte的低7位是用来存数值的位

(c)Varints方法小端字节序

ProtoBuf中消息的编码规则:

(a)每条消息都是有一系列的key-value对组成的, key和value分别采用不同的编码方式。

(b)对某一条件消息进行编码的时候,是把该消息中所有的key-value对序列化成二进制字节流;而解码的时候,解码程序读入二进制的字节流,解析出每一个key-value对,如果解码过程中遇到识别不出来的类型,直接跳过。这样的机制,保证了即使该消息添加了新的字段,也不会影响旧的编/解码程序正常工作。

(c)key由两部分组成,一部分是在定义消息时对字段的编号,另一部分是字段类型。

  (d) key的最后3个bits用于存储字段的类型信息。那么在使用该编码时,Protocol Buffer所支持的字段类型将不会超过2^3=8种。这里我们可以进一步计算出Protocol Buffer在一个消息中可以支持的字段数量为2^(32-3)-1;

注:在.proto文件中定义消息的字段标号时,可以是不连续的,但是如果将其定义为连续递增的数值,将获得更好的编码和解码性能。

C数组的序列化和反序列化API

SerializeToArray(void* data,int size) const;  //序列化

ParseFromArray(const void* data,int size);//反序列化

 

C++ String的序列化与发序列化API

SerializeToString(string* output) const;//序列化

ParseFromString(const string& data);//反序列化

 

文件描述的序列化与反序列化API

SerializeToFileDescriptro(int file_descriptro) const;//序列化

ParseFromFileDescriptro(int file_descriptro);//反序列化

文件操作:

iphone沙箱模型的四个文件夹:Documents目录、AppName.app目录、Library目录(Preferences目录和Caches目录)、temp目录

1)Documents目录:将所有的应用程序数据文件写入到这个目录下。这个目录用于存储用户数据或其他应该定期备份的信息。

2)AppName.app目录:这是应用程序的程序包目录,包含应用程序的本身。由于程序必须签名,所以在运行时不能对这个目录中的内容作修改,否则可能会导致应用程序无法启动。

3)Library目录:这个目录下有两个子目录:Caches和Perferences

    a. Preferences 目录

      包含应用程序的偏好设置文件。    

    b.  Caches 目录

      用于存放应用程序专用的支持文件,保存应用程序再次启动过程中需要的信息

4)temp目录

      用于存放临时文件,存放应用程序再次启动过程中需要的信息

 

获取文件目录路径的方法

一、获取Documents目录路径的方法

NSArray *path=NSSearchPathForDirectoriesInDomains(

                                      NSDocumentsDrectory,NSUserDomainMask,YES);

NSString *docDir=[path objectAtIndex:0];

二、获取Caches目录路径的方法

NSArray *path=NSSearchPathForDirectoriesInDomains(

                                     NSCachesDirectory,NSUserDomainMask,YES);

NSString *cachesDir=[paths objectAtIndex:0];

三、获取temp目录路径的方法

NSString *tempDir=NSTemporaryDirectory();