
本文共 4991 字,大约阅读时间需要 16 分钟。
Protobuf是谷歌开发的一套用于序列化结构数据、沟通协议、数据储存等用途的工具,具有语言无关性、平台无关性、可扩展性等优点。
用于通信时,只要通信双方都使用protobuf生成的协议代码,就可以用于将数据序列化为二进制数据或者进行解析。它本身支持C++、、。在安装了AS3版本的插件后,还可以用于flash平台与其他平台的数据沟通。
如何编写.proto文件,定义AS3与后端的协议内容 (这部分内容译自google的帮助文档)
protobuf来处理数据通信可以消除通信双方语言不通的问题,这里我们要通信的一方是flash,其语言是actionscript3.0。另一端可以是任何protobuf支持的语言,C++、JAVA等等。
首先要对通信的协议进行规定,也就是双方要传输什么样的信息,有哪几种信息,每种信息都包含了哪些数据。这些规定都将写在一个.proto为后缀的文件中(当然你想以其他的后缀结尾或者不用后缀也是没有问题的,只要在后面用到这个.proto文件的批处理文件中修改成对应的文件名就OK了)。
经过批处理文件调用proto文件后,将会生成对应平台的文件,这里我们需要生成.as文件。而通信的另一端也需要根据其平台生成对应的文件。
Proto文件的书写格式:
1、消息(Message)的书写
1 2 3 4 5 6 7 | // 客户端向服务器提交认证信息 message OP_C_AUTH_SESSION { required string account_name = 1; required string password = 2; optional int32 somenumber = 3 [ default = 0]; } |
上面就定义了一条由客户端发送给服务器端的登录验证消息,这条消息可以携带account_name、password和somenumber 三个字段的信息。
message开头表示这是定义了一个消息,message后面的字符串是这个消息的名称,他将作为生成的AS3类名,生成的消息类是com.netease.protobuf.Message的子类。
- 指定字段规则
required表示这个字段是必须具备的,以required定义的字段将在对应as3类中建立一个公共成员变量,例如上面的OP_C_AUTH_SESSION类将会拥有如下2个字段:
1 2 | public var accountName: String ; public var password: String ; |
optional表示其是可有可无的,以optional定义的字段将在对应的as3类中建立一个私有变量、对应的setter、getter、以及一个标识该字段是否存在的属性,还有一个清空该字段的方法。如下:
1 2 3 4 5 6 7 8 9 | private var somenumber$field: int ; public function clearSomenumber(): void ; public function get hasSomenumber(): Boolean ; public function set somenumber(value: int ): void ; public function get somenumber(): int ; |
假如在将来你希望把一个required的字段修改为一个optional的字段,那么接受消息的另一端也需要做对应修改,否则当检测到一个必须的字段不存在时会丢弃该数据包。
还有一种是以repeated打头的字段,这是用于一种可以重复任意次(包括0次)的字段,在生成的AS3文件中表现为一个Array。
- 指定字段类型
可以制定的基本字段类型见下表:
.proto类型 | 说明 | C++类型 | AS3类型 |
double | double | Number | |
float | float | Number | |
int32 | 使用可变长度编码。在处理负数时效率低下——假如该字段可能使用负数,用sint32替代。 | int32 | int |
int64 | 使用可变长度编码。在处理负数时效率低下——假如该字段可能使用负数,用sint64替代。 | int64 | Int64 |
uint32 | 使用可变长度编码。 | uint32 | uint |
uint64 | 使用可变长度编码。 | uint64 | UInt64 |
sint32 | 使用可变长度编码。有符号的整型,在编码负数时效率比普通int32更高。 | int32 | int |
sint64 | 使用可变长度编码。有符号的整型,在编码负数时效率比普通int64更高。 | int64 | Int64 |
fixed32 | 总是占据4个字节。当其值常常大于228时效率比uint32高。 | uint32 | uint |
fixed64 | 总是占据8个字节。当其值常常大于256时效率比uint64高。 | uint64 | UInt64 |
sfixed32 | 总是占据4个字节。 | int32 | int |
sfixed64 | 总是占据8个字节 | int64 | Int64 |
bool | bool | Boolean | |
string | 一个必须由utf-8或者7-bit ASCII编码的字符串 | string | String |
bytes | 可以用来存放任意的字节序列 | string | ByteArray |
上表中的AS3类型中的Uint64、Int64是自定义类。在com.netease.protobuf.*;包内,这个包的内容会自动被import到生成的消息类中。
除了这些基本类型之外,还可以使用枚举和其他message对象来为字段赋值。枚举即AS3中静态常量的用法,可以用有实际意义的常量来代替无意义的数值。
1 2 3 4 5 6 7 8 9 10 11 12 | message SearchRequest { enum Corpus { UNIVERSAL = 0; WEB = 1; IMAGES = 2; LOCAL = 3; NEWS = 4; PRODUCTS = 5; VIDEO = 6; } optional Corpus corpus = 4 [ default = UNIVERSAL]; } |
如果你想在一个消息内携带另一个子消息,则可以使用类型属于message的字段,如:
1 2 3 4 5 6 7 8 9 | message SearchResponse { repeated Result result = 1; } message Result { required string url = 1; optional string title = 2; repeated string snippets = 3; } |
上面代码中,Result这个消息就是SearchResponse消息的一个子消息,它作为父消息的一个字段而存在。
一个消息的非基础类型字段(enum和message)可以在3个地方定义:
- 与父消息并列定义,即上面的SearchResponse例子
- 定义在父消息体的内部: ,即上面的SearchRequest例子以及下面的例子
1 2 3 4 5 6 7 8 | message SearchResponse { message Result { required string url = 1; optional string title = 2; repeated string snippets = 3; } repeated Result result = 1; } |
这种称之为嵌套类型,可以嵌套message类和enum类。被嵌套的类会在生成AS3代码时新建一个与父级类同名的文件夹,并将嵌套的类生成在这个文件夹内。
3、定义在其他.proto文件中
如果想引用其他协议文件中的消息类,需要先将这个协议文件import到当前的协议文件中:
import “myproject/other_protos.proto”;
- 设置字段ID(Tag)
在required string account_name = 1;中,1就是这个字段的Tag,它是每个字段独一无二的ID,用于在序列化对象到二进制数据时做标识用。你可以设置的最小值为1,最大值为229 – 1或者说 536,870,911。但是不能使用从19000到19999这些数字,他们已经被protobuf自身所占用了。
Tag也同样在数据包中占据体积,1-15的Tag值只占用一个字节,而16-2047则会占用2个字节。所以应该将1-15提供给较频繁的消息字段使用。注意空出一些短数字留给以后可能添加的需要频繁使用的字段。
- 设置字段的缺省值
[default = 0]语法用于设置这个字段的缺省值。
2、枚举(enum)的书写
1 2 3 4 5 6 7 8 9 10 11 12 13 | // 服务器向客户端返回认证结果 enum AUTH_SESSION_CODES { AUTH_SESSION_OK = 1; AUTH_SESSION_FAILED = 2; AUTH_SESSION_REJECT = 3; AUTH_SESSION_SYSTEM_ERROR = 4; AUTH_SESSION_UNKNOWN_ACCOUNT = 5; AUTH_SESSION_INCORRECT_PASSWORD = 6; AUTH_SESSION_SESSION_EXPIRED = 7; AUTH_SESSION_WAIT_QUEUE = 8; AUTH_SESSION_BANNED = 9; } |
在AS3中没有枚举类,因此以上的proto代码将生成一个普通的object类。这个类有对应的静态成员变量,用以定义各种状态/消息,便于信息交互的双方理解。生成的AS3代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 | package utils.socket.protocols { public final class AUTH_SESSION_CODES { public static const AUTH_SESSION_OK: int = 1 ; public static const AUTH_SESSION_FAILED: int = 2 ; public static const AUTH_SESSION_REJECT: int = 3 ; public static const AUTH_SESSION_SYSTEM_ERROR: int = 4 ; public static const AUTH_SESSION_UNKNOWN_ACCOUNT: int = 5 ; public static const AUTH_SESSION_INCORRECT_PASSWORD: int = 6 ; public static const AUTH_SESSION_SESSION_EXPIRED: int = 7 ; public static const AUTH_SESSION_WAIT_QUEUE: int = 8 ; public static const AUTH_SESSION_BANNED: int = 9 ; } } |
于是可以在接受到服务器的返回的验证包后,对其authResult属性进行判断:
1 2 3 4 5 6 7 8 9 10 11 | var message:OP_S_AUTH_SESSION = new OP_S_AUTH_SESSION(); message .mergeFrom(bytes); //假设bytes是我们接收到的数据包 switch (message. authResult){ case AUTH_SESSION_CODES. AUTH_SESSION_OK: //do sth. break ; case AUTH_SESSION_CODES. AUTH_SESSION_FAILED: //do sth. break ; //… } |
3、还有继承、包等更多复杂格式的写法请看
发表评论
最新留言
关于作者
