准备工作
编译 Protobuf,需要安装工具:
brew install autoconf
brew install automake
brew install libtool
运行下面脚本进行编译:
./autogen.sh
./configure
make
make install
检查protobuf是否安装成功:
protoc --version
成功打印版本号则安装成功
libprotoc 3.21.12
Protobuf 支持的数据类型
Protobuf定义了一套基本数据类型
.proto Type | Notes | C Type |
---|---|---|
double | double | |
float | float | |
int32 | 使用变长编码,对于负值的效率很低,如果你的域有可能有负值,请使用sint64替代 | int32 |
uint32 | 使用变长编码 | uint32 |
uint64 | 使用变长编码 | uint64 |
sint32 | 使用变长编码,这些编码在负值时比int32高效的多 | int32 |
sint64 | 使用变长编码,有符号的整型值。编码时比通常的int64高效。 | int64 |
fixed32 | 总是4个字节,如果数值总是比总是比228大的话,这个类型会比uint32高效。 | uint32 |
fixed64 | 总是8个字节,如果数值总是比总是比256大的话,这个类型会比uint64高效。 | uint64 |
sfixed32 | 总是4个字节 | int32 |
sfixed64 | 总是8个字节 | int64 |
bool | bool | |
string | 一个字符串必须是UTF-8编码或者7-bit ASCII编码的文本。 | string |
bytes | 可能包含任意顺序的字节数据。 | string |
protobuf 枚举类型
当需要定义一个消息类型的时候,可能想为一个字段指定“预定义值序列”中的一个值,这时候可以通过枚举实现。
syntax = "proto3";//指定版本信息,不指定会报错
//枚举消息类型,使用enum关键词定义,一个电话类型的枚举类型
enum PhoneType {
MOBILE = 0; //proto3版本中,首成员必须为0,成员不应有相同的值
HOME = 1;
WORK = 2;
}
// 定义一个电话消息
message PhoneNumber {
string number = 1; // 电话号码字段
PhoneType type = 2; // 电话类型字段,电话类型使用PhoneType枚举类型
}
protobuf 数组类型
在protobuf消息中定义数组类型,是通过在字段前面增加repeated关键词实现,标记当前字段是一个数组。
整数数组的例子:
message Msg { // 只要使用repeated标记类型定义,就表示数组类型。 repeated int32 arrays = 1; }
字符串数组
message Msg { repeated string names = 1; }
protobuf 消息嵌套
我们在各种语言开发中类的定义是可以互相嵌套的,也可以使用其他类作为自己的成员属性类型。
在protobuf
中同样支持消息嵌套,可以在一个消息中嵌套另外一个消息,字段类型可以是另外一个消息类型。
- 引用其他消息类型的用法
// 定义Result消息
message Result {
string url = 1;
string title = 2;
repeated string snippets = 3; // 字符串数组类型
}
// 定义SearchResponse消息
message SearchResponse {
// 引用上面定义的Result消息类型,作为results字段的类型
repeated Result results = 1; // repeated关键词标记,说明results字段是一个数组
}
- 消息嵌套
类似类嵌套一样,消息也可以嵌套。
例子:
message SearchResponse {
// 嵌套消息定义
message Result {
string url = 1;
string title = 2;
repeated string snippets = 3;
}
// 引用嵌套的消息定义
repeated Result results = 1;
}
- import导入其他proto文件定义的消息
我们在开发一个项目的时候通常有很多消息定义,都写在一个proto文件,不方便维护,通常会将消息定义写在不同的proto文件中,在需要的时候可以通过import导入其他proto文件定义的消息。
例子:
保存文件: result.proto
syntax = "proto3";
// Result消息定义
message Result {
string url = 1;
string title = 2;
repeated string snippets = 3; // 字符串数组类型
}
保存文件: search_response.proto
syntax = "proto3";
// 导入Result消息定义
import "result.proto";
// 定义SearchResponse消息
message SearchResponse {
// 使用导入的Result消息
repeated Result results = 1;
}
protobuf map类型
protocol buffers
支持字典类型定义。
- map语法
map<key_type, value_type> map_field = N;
key_type
可以是任何整数或字符串类型(除浮点类型和字节之外的任何标量类型)。请注意,枚举不是有效的key_type
。
value_type
可以是除另一个映射之外的任何类型。
- map的例子
syntax = "proto3";
message Product {
string name = 1; // 商品名
// 定义一个k/v类型,key是string类型,value也是string类型
map<string, string> attrs = 2; // 商品属性,键值对
}
Map
字段不能使用repeated
关键字修饰。
定义消息
消息(message),在protobuf中指的就是我们要定义的数据结构
语法
syntax = "proto3";
message 消息名 {
消息体
}
syntax
关键词定义使用的是proto3
语法版本,如果没有指定默认使用的是proto2
。
message
关键词,标记开始定义一个消息,消息体,用于定义各种字段类型。
syntax = "proto3";
message Person {
string name = 1;
int32 age = 2;
string tel = 3;
}
定义了一个Person消息,这个消息有3个字段,name和tel是字符串类型,age是int32类型。
我们通常将protobuf消息定义保存在.proto为后缀的文件中。
分配标识符
在消息定义中,每个字段后面都有一个唯一的数字,这个就是标识号。
这些标识号是用来在消息的二进制格式中识别各个字段的,一旦开始使用就不能够再改变,每个消息内唯一即可,不同的消息定义可以拥有相同的标识号。
保留标识号(Reserved
)
如果你想保留一些标识号,留给以后用,可以使用下面语法:
message Person {
reserved 2, 15, 9 to 11; // 保留2,15,9到11这些标识号
}
如果使用了这些保留的标识号,protocol buffer编译器会输出警告信息。
注释
往.proto文件添加注释,支持C/C++/java风格的双斜杠(//) 语法格式。
例子:
message Person {
string name = 1; //名字
int32 age = 2; //年龄
string tel = 3;
}
为消息定义包
我们也可以为消息定义包。
proto协议文件编译为OC文件
protoc --proto_path=. --objc_out=. Person.proto
参数的形式为: --参数命令名=参数
--proto_path=.
指出proto文件所在的根目录是哪里, 如果用.说明是当前目录
--objc_out=.
指出 生成目录在哪里, 如果用.说明是当前目录
最后跟 proto文件的名称
iOS项目中使用
- 使用cocoapods引入protobuf
pod ‘Protobuf’
- 新建一个文件夹,拷贝已经有的.proto文件至此。用终端进入该文件夹,执行
protoc --plugin=/usr/local/bin/protoc-gen-objc *.proto --objc_out="./"
- 文件夹下便可以看到xxx.pb.h和xxxx.pb.m这两个文件了。将生成的 .h 和 .m 文件添加到工程中,编译
序列化
我们在使用socket与服务器通信时,是以二进制数据流的形式进行传输的,因此我们要将Pbdata.pbobjc创建的对象转为二进制数据流,这个过程就称之为序列化
反序列化
服务器给我们传输的也是二进制数据流,所以我们需要不断拼接数据包,直到拼接成服务器规定的大小,将其转为OC对象,那么这个过程就是反序列化