在as3中使用protobuf

  在最近参与开发的adobe air项目中,前后端的通信协议从XML、JSON再到protobuf,最后选择protobuf原因,主要是前后端维护protobuf协议就行了,同时还可以利用IDE做一些编译检查。目前我能找到的protobuf as3开源库,都存在一些问题:不支持嵌套类,生成代码无法编译等等。于是花了一点时间,参考google protobuf相关说明,编写protobuf-as3以及protoc-as3,用于支持运行时及代码生成,https://github.com/zhongfq/protobuf-as3

  protobuf-as3库只支持proto3格式,proto3列出的数据类型基本都支持。

  以下proto文件将生成5个as3类:Token.as、Token$Type.as、TokenBindingResponseCode.as、TokenBindingRequest.as、TokenBindingResponse.as。我们以‘$’作为分隔符以实现protobuf类的嵌套。

syntax = "proto3";
package user.token;

message Token {
    enum Type {
        NONE = 0;
        QQ = 1;
        WEIBO = 2;
        WECHAT = 3;
    }

    Type type = 1;
    string value = 2;
}

enum TokenBindingResponseCode {
    ERROR = 0;
    OK = 200;
    EXIST = 400;
}

message TokenBindingRequest {
    Token token = 1;
}

message TokenBindingResponse {
    TokenBindingResponseCode responseCode = 1;
    Token token = 2;
}
var token:Token = new Token();
token.type = Token$Type.QQ;
token.value = "xxxxxx";

var req:TokenBindingRequest = new TokenBindingRequest();
req.token = token;

protobuf数据不包含任何的元数据,所以对于一段数据而言,你是不能够区别这段数据是属于哪一条协议的,因此对于前后端的交互,我引入12字节的头部(length、session、type)。

package network {

import flash.utils.ByteArray;

internal class Packet {
    private var _session:int;
    private var _type:int;
    private var _data:ByteArray;

    public function Packet(session:int, type:int, data:ByteArray) {
        _session = session;
        _type = type;
        _data = data;
        _data.position = 0;
    }

    public function get session():int {
        return _session;
    }

    public function get type():int {
        return _type;
    }

    public function get data():ByteArray {
        return _data;
    }
}
}
package network {

import flash.net.Socket;
import flash.utils.ByteArray;
import flash.utils.Endian;

internal class PacketBuffer {
    //length + session + type
    private static const HEADER_LENGTH:int = 12;
    private var _length:int = 0;
    private var _position:int = 0;
    private var _packet:Packet = null;

    public function PacketBuffer() {
    }

    public function pack(socket:Socket):Packet {
        // 消息长度至少为12, length + session + type
        if (socket.bytesAvailable <= 0 || (_length == 0 && socket.bytesAvailable < HEADER_LENGTH)) {
            return null;
        }

        socket.endian = Endian.BIG_ENDIAN;

        if (_length == 0) {
            _length = socket.readInt();

            // valid packet: length + session + type >= 12
            if (_length < HEADER_LENGTH) {
                socket.close();
                return null;
            }

            var session:int = socket.readInt();
            var type:int = socket.readInt();

            _position = 0;
            _packet = new Packet(session, type, new ByteArray());
        }

        if (socket.bytesAvailable > 0 || _length == HEADER_LENGTH) {
            var len:int = Math.min(_length - HEADER_LENGTH - _position, socket.bytesAvailable);
            socket.readBytes(_packet.data, _position, len);
            _position += len;

            if (_position == _length - HEADER_LENGTH) {
                var ret:Packet = _packet;

                _packet = null;
                _position = 0;
                _length = 0;

                return ret;
            }
        }

        return null;
    }
}
}
syntax = "proto3";

enum CommandCode {
    // gateway
    GATEWAY             = 0x0000;

    // login
    SIGN_IN             = 0x0100;
    CHECK_ACCOUNT       = 0x0101;
    SIGN_UP             = 0x0102;
    QUICK_ENROLL        = 0x0103;

    // user
    USER_MODIFIER       = 0x0200;
    TOKEN_BINDING       = 0x0201;
    USER_STAT           = 0x0202;
    USER_AVATAR         = 0x0203;
}

CommandCode作为数据包的类型标识,用于上面所说头部中的类型段。

对于发送一个数据包,方式如下:

private function sendPacket(packet:Packet):void {
    // length + session + type
    _socket.endian = Endian.BIG_ENDIAN;
    _socket.writeInt(packet.data.length + 12);
    _socket.writeInt(packet.session);
    _socket.writeInt(packet.type);
    _socket.writeBytes(packet.data);
    _socket.flush();
}

对于接收数据包,方式如下:

private function socketDataHandler(event:ProgressEvent):void {
    var packet:Packet;
    while ((packet = _packetBuffer.pack(_socket)) != null) {
        dispatchPacket(packet);
    }
}

session变量由服务器维护,服务器每发送一条用户数据,session就+1,前端断线重连时,发送session与服务作对比,如果一样,就是执行断线重连,如果不一样就强制走登录流程。

转载于:https://www.cnblogs.com/zhongfq/p/5663106.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
在 Pinus 使用 ProtobufProtocol 可以通过以下步骤实现: 1. 安装 protobufjs 和 protocol-buffers 库: ``` npm install protobufjs npm install protocol-buffers ``` 2. 在 Pinus 项目配置 protobufprotocol: 在 Pinus 项目的根目录下创建一个 `protobuf` 目录,并在该目录定义 `.proto` 文件。 例如,在 `protobuf` 目录创建 `player.proto` 文件,定义一个 `Player` 类型: ``` syntax = "proto3"; message Player { int32 id = 1; string name = 2; int32 level = 3; } ``` 3. 使用 protobufjs 将 `.proto` 文件编译为 JavaScript 模块: 在 `protobuf` 目录运行以下命令: ``` pbjs -t static-module -w commonjs -o player.js player.proto ``` 该命令将 `player.proto` 编译为 `player.js` 模块。在该模块,`Player` 类型会被定义为一个 JavaScript 类。 4. 在 Pinus 项目使用 protobufprotocol: 在 Pinus 项目的 `app.ts` 文件使用以下代码初始化 protobufprotocol: ``` import * as protobuf from 'protobufjs'; import * as protocol from 'protocol-buffers'; // 编译 protobuf 文件 const playerProto = protobuf.loadSync('./protobuf/player.js'); const playerType = playerProto.lookupType('Player'); // 初始化 protocol const encode = protocol(playerProto); const decode = protocol(playerProto); // 创建 Pinus 应用 const app = pinus.createApp({}); // 添加路由 app.route('player', (session, msg, next) => { // 将消息解码为 Player 对象 const player = decode(msg); // 处理消息 console.log(`Received player message: ${JSON.stringify(player)}`); // 将 Player 对象编码为消息 const encodedPlayer = encode(player); // 发送响应消息 next(null, encodedPlayer); }); // 启动 Pinus 应用 app.start(); ``` 在以上代码使用 `protobufjs` 编译了 `player.proto` 文件,并使用 `protocol-buffers` 初始化了 `encode` 和 `decode` 函数。在 Pinus 应用的路由处理器使用 `decode` 函数将收到的消息解码为 `Player` 对象,并使用 `encode` 函数将 `Player` 对象编码为消息。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值