google protobuf与as3的通信协议

Protobuf是谷歌开发的一套用于序列化结构数据、沟通协议、数据储存等用途的工具,具有语言无关性、平台无关性、可扩展性等优点。

用于通信时,只要通信双方都使用protobuf生成的协议代码,就可以用于将数据序列化为二进制数据或者进行解析。它本身支持C++、JavaPython。在安装了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个地方定义:

  1. 与父消息并列定义,即上面的SearchResponse例子
  2. 定义在父消息体的内部: ,即上面的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、还有继承、包等更多复杂格式的写法请看

https://developers.google.com/protocol-buffers/docs/proto

猜你喜欢

转载自blog.csdn.net/dingqinghui/article/details/74910908