当前位置: 首页 > 工具软件 > Goddess > 使用案例 >

creator游戏《Protecive goddess》(一)-- 网络通信协议sproto

秦英发
2023-12-01

本系列主要是对此次项目从0~1的一个复盘

一来方便自己后期回顾,暴露问题

二来希望可以给新入坑的朋友们一些帮助

不会讲的太细,主要是提供一个思路和大的框架,话不多说,我们开始吧。

项目中如果有不对的地方或者有更好的解决方案,欢迎大家指正!


一、sproto介绍

之前项目都是用的protobuf,此次使用sproto主要原因如下:
① 项目初期定的客户端跟服务器都是用lua开发
② 服务端基于skynet框架开发,而sproto正是由skynet提供的一套协议,它类似 protobuf ,但设计更加简洁,也更利于 lua 使用

官方介绍:https://github.com/cloudwu/skynet/wiki/Sproto


二、准备工作

sprotojs插件:https://gitee.com/zwguang/sproto-js

插件准备:文档中有案例介绍可以自行了解下,项目中只需要引入sproto.js、utils.js这两个文件
协议文件准备: 服务器会提供一个xxx.sproto源文件,将其转换为.spb就可以使用了
协议加载:之前的一篇文章有过记录https://blog.csdn.net/zwg739424406/article/details/111239661


三、协议解析

客户端跟服务器商定好每条消息的结构之后,便可以进行对应的 拆包封包 的工作了。
我们目前的消息结构如下

    /*   Head
     *   2 byte是包头,读取后面body的size
     *   body
     *   2 byte eventid
     *   2 byte datasize
     *   n byte data -- 这个data才是用sproto解析出来的,(pakename, data),pakename是对应的数据结构名字(或者说是table变量的变量名)
     */

核心的拆包和封包代码如下,主要通过对协议加载得到的 m_sproto 进行 encode 和 decode,与之对应的还有 pencode 和 pdecode ,带p的意思是对封好的数据包进一步的压缩,以减少消息的包体。

文中的 sprotoMap 存储的是协议eventid与pakeName的相互映射

/**
     * 数据封包 发送
     * @param eventId 
     * @param data 
     */
    public static encodeBuffer(eventId: number, data: any) {
        let id: number = eventId;
        let protoName = sprotoMap[id.toString()]
         
        let dataArr = this.m_sproto.pencode(protoName, data);
        let dataBuff = this.arr2Buffer(dataArr); 

        // printf.Log("eventId:", eventId, "protoName:" , protoName, "  datass:" , datass)
        // printf.Log("dataBuff:", dataBuff, " dataBuff.byteLength:" , dataBuff.byteLength)

        let datasizeBuff = this.createBuffer(dataBuff.byteLength);
        let eventidBuff = this.createBuffer(eventId);
        let bodyBuff = this.ConnectArrayBuff(eventidBuff, datasizeBuff, dataBuff);
        let headBuff = this.createBuffer(bodyBuff.byteLength);

        return this.ConnectArrayBuff(headBuff, bodyBuff);
    }

    /**
     * 数据解包 派发
     * @param buffer 
     */
    public static decodeBuffer(buffer) {
        let view: DataView = new DataView(buffer);
        let dataBuff = buffer.slice(6, buffer.length)
        let eventId = view.getInt16(2, false)

        let name = sprotoMap[eventId];
        let data = this.m_sproto.pdecode(name, new Uint8Array(dataBuff));
        let message = new sprotoMessage()
        message.pakeName = name
        message.body = data
        
        return message;
    }

    private static arr2Buffer(arr){
        let len = arr.length;
        let arrBuffer = new Uint8Array(len);
        for(let i=0; i<len; i++){
            arrBuffer[i] = arr[i];
        }
        
        return arrBuffer.buffer;
    }

    private static createBuffer(data) {
        var _typeBuff = new ArrayBuffer(2);
        var total = data;

        var view = new DataView(_typeBuff);
        view.setInt16(0, total, false);
        return _typeBuff;
    }

    /**
     * 字节头和数据组合成arrayBuff
     * @param args 字节头和数据buff
     */
    private static ConnectArrayBuff(...args: any[]): ArrayBuffer {
        var length = 0;
        var buffer = null;

        for (let i = 0; i < args.length; i++) {
            buffer = args[i];
            length += buffer.byteLength;
        }

        var joined = new Uint8Array(length);
        var offset = 0;
        for (let i = 0; i < args.length; i++) {
            buffer = args[i];
            joined.set(new Uint8Array(buffer), offset);
            offset += buffer.byteLength;

        }
        return joined.buffer;
    };
 类似资料: