各位亲 有时间可以去看看我的 “金骏家居淘宝店” 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();