• 使用cocoscreator + node.js + websocket实现简单的聊天服务


    先上个效果图:

    使用cocoscreator 1.9.1 + node.js + websocket实现,没有使用socket.io, 全部自己封装,长连接进行封装后可以和短连接使用方法一样,使用简单,方便以后开发网络游戏。

    1、客户端:

      主要就是聊天内容的显示,自动换行和背景扩展,代码大概如下:

      

    cc.Class({
        extends: cc.Component,
    
        properties: {
            msgLabel: cc.Label,
            uidLabel: cc.Label,
            msgLayout: cc.Layout,
            msgBg: cc.Node,
            maxLen: 500,
        },
    
        // LIFE-CYCLE CALLBACKS:
    
        // onLoad () {},
    
        start () {
            this.node.runAction(cc.fadeTo(0.5, 255))
        },
    
        initMsg(msg, uid){
            this.msgLabel.string = msg;
            this.uidLabel.string = uid;
    
            this.msgLabel.overflow = cc.Label.Overflow.NONE;
            // this.msgBg.width = this.msgLabel.node.width + 10;
            // this.msgBg.height = this.msgLabel.node.height + 10;
            // this.node.height = this.msgBg.height + 40;
    
            this.scheduleOnce((dt)=>{
                if ( this.msgLabel.node.width >= this.maxLen){
                    this.msgLabel.overflow = cc.Label.Overflow.RESIZE_HEIGHT;
                    this.msgLabel.node.width = this.maxLen;
                }
    
                this.msgBg.width = this.msgLabel.node.width + 10;
                this.msgBg.height = this.msgLabel.node.height + 10;
                this.node.height = this.msgBg.height + 40;
    
            }, 0);
    
            this.node.opacity = 0;
        }
    
        // update (dt) {},
    });

      网络部分分成了四层:

        1、socket 封装基础的websocket, 这里是最底层,也是真正链接的开始

        2、network 控制socket链接层,实现各回调接口

        3、netproxy 封装各服务功能,把长连接变成短连接的请求方式

        4、netprotocols 和服务器协商,确定每个请求的请求体格式和回复格式

      各部分代码如下:

      GameWebSocket.js:  

    /**
     * @enum {number}
     */
    var GameWebSocketState = cc.Enum({
        CONNECTING: 1,
        OPEN: 2,
        CLOSING: 3,
        CLOSED: 4
    });
    
    /**
     * @interface
     */
    var GameWebSocketDelegate = cc.Class({
    
        onSocketOpen: function () {
    
        },
    
        /**
         * 收到了消息
         * @param {string|Uint8Array} data
         */
        onSocketMessage: function (data) {
    
        },
    
        onSocketError: function () {
    
        },
    
        /**
         * 连接关闭
         * @param {string} reason
         */
        onSocketClosed: function (reason) {
    
        }
    });
    
    /**
     * @interface
     */
    var GameWebSocketInterface = cc.Class({
    
        connect: function () {
    
        },
    
        send: function () {
    
        },
    
        close: function () {
    
        },
    
        getState: function () {
    
        }
    });
    
    var GameWebSocket = cc.Class({
        extends: GameWebSocketInterface,
    
        properties: {
    
            /**
             * @type {String} 服务器地址
             */
            _address: null,
    
            /**
             * @type {GameWebSocketDelegate}
             */
            _delegate: null,
    
            /**
             * @type {WebSocket}
             */
            _webSocket: null,
        },
    
        /**
         * @param {string} address 服务器地址
         * @param {GameWebSocketDelegate} delegate 回调接口
         */
        init: function(address, delegate){
            this._address = address;
            this._delegate = delegate;
            this._webSocket = null;
        },
    
        connect: function () {
            cc.log('connect to '+ this._address);
    
            var ws = this._webSocket = new WebSocket(this._address);
            ws.onopen = this._delegate.onSocketOpen.bind(this._delegate);
            ws.onmessage = function (param) {
                this._delegate.onSocketMessage(param.data);
            }.bind(this);
            ws.onerror = this._delegate.onSocketError.bind(this._delegate);
            // function({code: Number, reason: String, wasClean: Boolean})}
            ws.onclose = function (param) {
                this._delegate.onSocketClosed(param.reason);
            }.bind(this);
        },
    
        /**
         * 发送数据
         * @param {string|Uint8Array} stringOrBinary
         */
        send: function (stringOrBinary) {
            this._webSocket.send(stringOrBinary);
        },
    
        close: function () {
            if (!this._webSocket) {
                return;
            }
    
            try {
                this._webSocket.close();
            } catch (err) {
                cc.log('error while closing webSocket', err.toString());
            }
            this._webSocket = null;
        },
    
        getState: function () {
            if (this._webSocket) {
                switch(this._webSocket.readyState){
                    case WebSocket.OPEN:
                        return GameWebSocketState.OPEN;
                    case WebSocket.CONNECTING:
                        return GameWebSocketState.CONNECTING;
                    case WebSocket.CLOSING:
                        return GameWebSocketState.CLOSING;
                    case WebSocket.CLOSED:
                        return GameWebSocketState.CLOSED;
                }
            }
            return GameWebSocketState.CLOSED;
        }
    });
    
    module.exports = {
        GameWebSocketState: GameWebSocketState,
        GameWebSocketDelegate: GameWebSocketDelegate,
        GameWebSocketInterface: GameWebSocketInterface,
        GameWebSocket: GameWebSocket
    };

      GameNetwork.js

    /**
     * Created by skyxu on 2018/10/9.
     */
    
    "use strict";
    
    let GameWebSocket = require("./GameWebSocket");
    let GameProtocols = require("./GameProtocols");
    
    /**
     * 服务器回复消息状态,判断回复消息的各种问题
     */
    var response_state = {
        ERROR_OK : '0'
    };
    
    /**
     * 请求回调对象,收到服务器回调后的回调方法
     */
    var NetworkCallback = cc.Class({
    
        properties: {
    
            /**
             * @type {BaseRequest} request
             */
            request: null,
    
            /**
             * 请求回调对方法
             */
            callback: null
        },
    
        /**
         * @param {BaseRequest} request
         * @param {function(BaseResponse): boolean} callback
         */
        init: function (request, callback) {
            this.request = request;
            this.callback = callback;
        }
    });
    
    
    let GameNetwork = cc.Class({
        extends: GameWebSocket.GameWebSocketDelegate,
    
        ctor: function() {
            this._socket = null;
    
            this._delegate = null;
    
            /**
             * 每次发送请求,都需要有一个唯一的编号
             * @type {number}
             * @private
             */
            this._requestSequenceId = 0;
    
            /**
             * 接受服务器主动下发的response回调
             * key 表示BaseResponse.act
             * @type {Object.<string, function(object.<string, *>)>}
             */
            this.pushResponseCallback = {};
    
            /**
             * 根据seq保存Request和其callback,以便在收到服务器的响应后回调
             * @type {Object.<int, NetworkCallback>}
             * @private
             */
            this._networkCallbacks = {};
        },
    
        setDelegate: function (delegate) {
            this._delegate = delegate;
        },
    
        /**
         * 注册服务器主动推送的response 回调
         */
        registerPushResponseCallback : function(act, callback){
            this.pushResponseCallback[act] = callback;
        },
    
        /**
         * 判断socket已连接成功,可以通信
         * @returns {boolean}
         */
        isSocketOpened: function(){
            return (this._socket && this._socket.getState() == GameWebSocket.GameWebSocketState.OPEN);
        },
    
        isSocketClosed: function () {
            return this._socket == null;
        },
    
        /**
         * 启动连接
         */
        connect: function (url) {
            cc.log("webSocketUrls=" + url);
            this._requestSequenceId = 0;
            this._socket = new GameWebSocket.GameWebSocket();
            this._socket.init(url, this);
            this._socket.connect();
        },
    
        closeConnect: function () {
            if(this._socket){
                this._socket.close();
            }
        },
    
        onSocketOpen: function () {
            cc.log('Socket:onOpen');
            if(this._delegate && this._delegate.onNetworkOpen){
                this._delegate.onNetworkOpen();
            }
        },
    
        onSocketError: function () {
            cc.log('Socket:onError');
        },
    
        onSocketClosed: function (reason) {
            cc.log('Socket:onClose', reason);
            if (this._socket) {
                this._socket.close();
            }
            this._socket = null;
    
            if(this._delegate && this._delegate.onNetworkClose){
                this._delegate.onNetworkClose();
            }
        },
    
        onSocketMessage: function (msg) {
            this._onResponse(msg);
        },
    
        _onResponse: function(responseData){
            cc.log('response->resp:', responseData);
            var responseJson = JSON.parse(responseData);
            var responseClass = GameProtocols.response_classes[responseJson.act];
            /**
             * @type {object.<BaseResponse>}
             */
            var response = new responseClass();
            response.loadData(responseJson.data);
            response.act = responseJson.act;
            response.seq = responseJson.seq;
            response.err = responseJson.err;
            response.ts = responseJson.ts;
    
            // 如果指定了回调函数,先回调
            var ignoreError = false;
            if(response.seq != -1){
                // 处理服务器推送消息
                var pushCallback = this.pushResponseCallback[response.act];
                if(pushCallback){
                    pushCallback(response);
                }
    
                // request回调
                var callbackObj = this._networkCallbacks[response.seq];
                if(callbackObj){
                    ignoreError = callbackObj.callback(response);
                    // try {
                    //     ignoreError = callbackObj.callback(response);
                    // } catch (err) {
                    //     cc.log(err + " error in response callback of " + response.act);
                    // } finally {
                    //     delete this._networkCallbacks[response.seq];
                    // }
                }
            }
    
            //有错,且不忽略,则统一处理错误
            if(response.err && response.err != response_state.ERROR_OK && !ignoreError){
                if (response.is_async) {  // 异步请求,如果出错了,应该需要重新登录
                    // todo 重新登录?或者重新同步数据?
                } else {  // 同步请求,如果出错了,需要显示错误信息
                    // todo 显示错误
                    var msg = responseJson.msg;
                    cc.log('server err ' + msg);
                }
            }
        },
    
        /**
         * 向服务器发送请求。
         *
         * 如果提供了callback,在收到response后会被回调。如果response是一个错误(status!=ERR_OK),则需要决定由谁来负责处理错误。
         * 如果callback中已经对错误进行了处理,应该返回true,这样会忽略该错误。否则应该返回false,则负责处理该错误。
         *
         * 特别注意:如果这是一个异步(is_async)请求,且出错,一般来讲应该重新登录/同步。但是如果callback返回了true,不会进行
         * 任何处理,也就是不会重新登录/同步。请小心确定返回值。
         *
         * @param {object.<BaseRequest>}
         * @param {function(BaseResponse): boolean=} opt_callback 回调函数。出错的情况下,如果返回true,则不会再次处理错误。
         */
        sendRequest: function (request, opt_callback) {
            // 每个请求的seq应该唯一,且递增
            request.seq = ++this._requestSequenceId;
    
            //生成NetworkCallback对象,绑定请求seq和回调方法
            if(opt_callback){
                this._networkCallbacks[request.seq] = new NetworkCallback();
                this._networkCallbacks[request.seq].init(request, opt_callback);
            }
            this._sendSocketRequest(false, request);
        },
    
        /**
         * sendRequest的不发送data字段
         */
        sendRequestNoData: function (request, opt_callback) {
            // 每个请求的seq应该唯一,且递增
            request.seq = ++this._requestSequenceId;
    
            //生成NetworkCallback对象,绑定请求seq和回调方法
            if(opt_callback){
                this._networkCallbacks[request.seq] = new NetworkCallback();
                this._networkCallbacks[request.seq].init(request, opt_callback);
            }
            this._sendSocketRequest(true, request);
        },
    
        /**
         * @param {Boolean} isNoData
         * @param {object.<BaseRequest>} req
         */
        _sendSocketRequest: function (isNoData, req) {
            cc.assert(this._socket);
    
            if (this.isSocketOpened()){
                //通过json的方法生成请求字符串
                var msg = null;
                if(isNoData){
                    msg = JSON.stringify({seq:req.seq, act:req.act});
                }else{
                    msg = JSON.stringify({seq:req.seq, act:req.act, data:req});
                }
                cc.log("WebSocketDelegate::send->" + msg);
                this._socket.send(msg);
            } else{
                // todo
            }
        }
    });
    
    module.exports = GameNetwork;

      GameProtocols.js

    /**
     * Created by skyxu on 2018/10/9.
     */
    
    "use strict";
    
    /**
     * 消息基类对象,请求消息BaseRequest, 回调消息BaseResponse都继承BaseProtocol
     */
    let BaseProtocol = cc.Class({
        ctor: function () {
            /**
             * 请求动作类型
             */
            this.act = '';
    
            /**
             * 每个请求的sequence_id应该唯一
             */
            this.seq = 0;
    
            /**
             * 错误代码,0为正常
             */
            this.err = 0;
    
            /**
             * 是否需要等待服务器回调
             */
            this.is_async = false;
        }
    });
    
    /**
     * 请求消息基类,客户端的请求都继承这个类
     */
    let BaseRequest = cc.Class({
        extends: BaseProtocol
    });
    
    /**
     * 服务器返回的消息对应的对象,包含返回数据,一般和BaseRequest成对使用
     * @class BaseResponse
     * @extends BaseProtocol
     */
    let BaseResponse = cc.Class({
        extends: BaseProtocol,
    
        /**
         * 读取返回数据,设置BaseResponse对象
         */
        loadData: function (data) {
            var key;
            for (key in data) {
                if(!this.hasOwnProperty(key)){
                    continue;
                }
    
                if(data[key] !== undefined && data[key] !== null){
                    this[key] = data[key];
                }
            }
        }
    });
    
    let HeartRequest = cc.Class({
        extends: BaseRequest,
        ctor(){
            this.act = 'heart';
            this.t = -1;    // 发送时间
        }
    });
    
    let HeartResponse = cc.Class({
        extends: BaseResponse,
    
        ctor(){
            this.act = 'heart';
            this.t = -1;
        }
    });
    
    let ChatRequest = cc.Class({
        extends: BaseRequest,
        ctor(){
            this.act = 'chat';
            this.msg = '';
            this.uid = '';
        }
    });
    
    let ChatResponse = cc.Class({
        extends: BaseResponse,
        ctor(){
            this.act = 'chat';
            this.msg = '';
            this.uid = '';
        }
    });
    
    let LoginRequest = cc.Class({
        extends: BaseRequest,
    
        ctor: function () {
            this.act = 'login';
    
            /**
             * facebook用户的accessToken,或游客的UUID
             */
            this.token = '';
    
            /**
             * token来源,默认0:游客,1:facebook
             */
            this.origin = 0;
    
            /**
             * 平台: 必须为以下几种之一:android/ios/winphone/pc
             */
            this.os = '';
    
            /**
             * 平台系统版本
             */
            this.osVersion = '';
    
            /**
             * 设备产品型号, 示例 iPhone8,2, SM-G 9280
             */
            this.deviceModel = '';
    
            /**
             * 渠道ID
             */
            this.channelId = 0;
    
            /**
             * Ios设备广告标示符
             */
            this.idfa = '';
    
            /**
             * 安卓设备id
             */
            this.androidId = '';
    
            /**
             * Google广告平台账号,安装了google play的设备可取到
             */
            this.googleAid = '';
    
            /**
             * 应用版本号
             */
            this.appVersion = '';
    
            /**
             * 取package name或者bundle id
             */
            this.packName = '';
    
    
            /**
             * 设备语言
             * @type {string}
             */
            this.language = '';
    
            this.locale = "";
    
        }
    });
    
    let LoginResponse = cc.Class({
        extends: BaseResponse,
    
        ctor: function () {
            this.act = 'login';
    
            /**
             * 游客第一次登录时返回的token,需要客户端保存
             */
            this.token = '';
    
            /**
             * 离体力下次恢复点的剩余时间秒数
             * @type {number}
             */
            this.spStepLeftTime = 0;
    
            /**
             * 体力恢复周期
             * @type {Number}
             */
            this.spInterval = 0;
    
            /**
             * 农场每天产出量,产出未解锁时为-1
             * @type {number}
             */
            this.farmDailyOut = -1;
    
            /**
             * 农场已产出量
             * @type {number}
             */
            this.farmCoins = 0;
    
            /**
             * 农场产出间隔
             * @type {number}
             */
            this.farmInterval = null;
    
            /**
             * 用json object表示的一个player对象,字段说明参见player json对象
             */
            this.me = {};
    
            /**
             * 建筑数据数组
             * @type {Array}
             */
            this.buildings = [];
    
            /**
             * 农民数据数组
             * @type {Array}
             */
            this.farms = [];
    
            /**
             * 富豪数据
             */
            this.cashking = {};
    
            /**
             * 行星配置
             */
            this.planetConf = {};
    
            /**
             * 农民配置
             */
            this.farmConfList = [];
    
            /**
             * 其他配置
             */
            this.settingConf = {};
    
            /**
             * 好友数据
             */
            this.friends = [];
    
            /**
             * 好友通缉的目标列表
             */
            this.helpWantList = [];
    
            /**
             * 邮件消息列表
             */
            this.newsList = [];
    
            /**
             * 复仇列表
             */
            this.revengeList = [];
    
            /**
             * 商品信息
             * @type {Array}
             */
            this.rechargeConfs = [];
    
            /**
             * 总岛数
             * @type {Number}
             */
            this.planetConfListSize = 0;
    
            /**
             * 他人行星信息对象,仅在转到fire断线重新登录时有效
             * @type {Object}
             */
            this.fireTarget = null;
    
            /**
             * 他人行星信息对象列表,仅在转到steal断线重新登录时有效
             * @type {Array}
             */
            this.stealTarget = null;
        }
    });
    
    let LogoutRequest = cc.Class({
        extends: BaseRequest,
    
        ctor: function () {
            this.act = 'logout';
        }
    });
    
    let LogoutResponse = cc.Class({
        extends: BaseResponse,
    
        ctor: function () {
            this.act = 'logout';
        }
    });
    
    /**
     * 绑定fb账号
     * @extends BaseRequest
     */
    let BindFacebookRequest = cc.Class({
        extends: BaseRequest,
    
        ctor: function () {
            this.act = 'bindFb';
    
            /**
             * facebook用户的accessToken,或游客的UUID
             */
            this.token = '';
        }
    });
    /**
     * 绑定fb账号
     * @extends BaseResponse
     */
    let BindFacebookResponse = cc.Class({
        extends: BaseResponse,
    
        ctor: function () {
            this.act = 'bindFb';
    
            /**
             * fb数据
             */
            this.me = 0;
    
            /**
             * fb好友
             */
            this.friends = 0;
        }
    });
    
    let SpinRequest = cc.Class({
        extends: BaseRequest,
    
        ctor: function () {
            this.act = 'spin';
    
            /**
             * 倍数
             * @type {Number}
             */
            this.x = 1;
        }
    });
    
    let SpinResponse = cc.Class({
        extends: BaseResponse,
    
        ctor: function () {
            this.act = 'spin';
    
            /**
             * 摇中的转盘ID
             */
            this.hit = 0;
    
            /**
             * 转到护盾,但护盾已满时,存在
             * @type {number}
             */
            this.shieldfull = 0;
    
            /**
             * 玩家数据对象
             */
            this.me = {};
    
            /**
             * 他人行星信息对象,仅在转到fire时有效
             * @type {*}
             */
            this.fireTarget = {};
    
            /**
             * 偷取对象数据
             */
            this.stealTarget = [];
    
            /**
             * 离体力下次恢复点的剩余时间秒数
             * @type {number}
             */
            this.spStepLeftTime = 0;
    
            /**
             * 体力恢复周期
             * @type {Number}
             */
            this.spInterval = 0;
    
            /**
             * 倍数
             * @type {Number}
             */
            this.x = 1;
        }
    });
    
    /**
     * 获取排名
     * @extends BaseRequest
     */
    let RankRequest = cc.Class({
        extends: BaseRequest,
    
        ctor: function () {
            this.act = 'rankboard';
    
            /**
             * 请求动作类型{ 0全部,1本地,2好友 }
             * @type {int}
             */
            this.type = 0;
        }
    });
    /**
     * 获取排名
     * @extends BaseResponse
     */
    let RankResponse = cc.Class({
        extends: BaseResponse,
    
        ctor: function () {
            this.act = 'rankboard';
    
            /**
             *  我的排名
             */
            this.myRank = 0;
    
            /**
             * 排名玩家数据
             */
            this.men = [];
        }
    });
    
    
    //push------------------------------------------------------------------------------
    
    /**
     * 推送消息 被攻击
     * @extends BaseResponse
     */
    var PushAttackedResponse = cc.Class({
        extends: BaseResponse,
    
        ctor: function () {
            this.act = 'attacked';
    
            /**
             * 玩家更新数据
             */
            this.me = null;
    
            /**
             * 建筑数据
             */
            this.building = null;
    
            /**
             * 敌人
             */
            this.hatredman = null;
    
            /**
             * 消息
             */
            this.news = null;
        }
    });
    
    
    /**
     * 推送消息 推送消息好友已赠送体力
     * @extends BaseResponse
     */
    var PushSendSpResponse = cc.Class({
        extends: BaseResponse,
    
        ctor: function () {
            this.act = 'sendSpNotify';
    
            /**
             * 好友对象
             */
            this.friend = null;
        }
    });
    
    /**
     * 推送消息 推送消息好友已领取赠送的体力
     * @extends BaseResponse
     */
    var PushTakeSpResponse = cc.Class({
        extends: BaseResponse,
    
        ctor: function () {
            this.act = 'takeSpNotify';
    
            /**
             * 好友对象
             */
            this.friend = null;
        }
    });
    
    /**
     * 推送消息 同步好友信息
     * @extends BaseResponse
     */
    var PushSyncFriendInfo = cc.Class({
        extends: BaseResponse,
    
        ctor: function () {
            this.act = 'friendInfoSync';
    
            /**
             * 好友
             */
            this.friend = null;
        }
    });
    
    /**
     * 推送消息 新增好友
     * @extends BaseResponse
     */
    var PushAddNewFriend = cc.Class({
        extends: BaseResponse,
    
        ctor: function () {
    
            this.act = 'newFriend';
    
            /**
             * 好友
             */
            this.friend = null;
    
            /**
             * 消息
             */
            this.news = null;
        }
    });
    
    /**
     * debug回调
     * @extends BaseRequest
     */
    let DebugChangeMeRequest = cc.Class({
        extends: BaseRequest,
    
        ctor: function () {
    
            this.act = "cmdTest";                    //请求动作类型
            this.cmd = "";
            //  "player coins add 100", cmd格式:player field value 或者 player field add value
            //  Building field [add] value where playerId value type value
        }
    
    });
    /**
     * debug回调
     * @extends BaseResponse
     */
    let DebugChangeMeResponse = cc.Class({
        extends: BaseResponse,
    
        ctor: function () {
            this.act = "cmdTest";
    
            /**
             * 玩家数据
             * @type {Object}
             */
            this.me = {};
    
            /**
             * 体力恢复周期
             * @type {Number}
             */
            this.spInterval = null;
    
            /**
             * 体力恢复剩余时间
             * @type {Number}
             */
            this.spStepLeftTime = null;
    
            /**
             * 存钱罐速度
             * @type {Number}
             */
            this.farmDailyOut = null;
    
            /**
             * 存钱罐可回收金币
             * @type {Number}
             */
            this.farmCoins = null;
    
            /**
             * 存钱罐回收周期
             * @type {Number}
             */
            this.farmInterval = null;
    
            /**
             * 岛屿建筑数据
             * @type {Array}
             */
            this.buildings = null;
        }
    });
    
    let response_classes = {
        login: LoginResponse,
        logout: LogoutResponse,
        spin: SpinResponse,
        bindFb: BindFacebookResponse,
        rankboard: RankResponse,
        heart: HeartResponse,
        chat: ChatResponse,
    
        //push
        attacked: PushAttackedResponse,
        sendSpNotify: PushSendSpResponse,
        takeSpNotify: PushTakeSpResponse,
        newFriend: PushAddNewFriend,
        friendInfoSync: PushSyncFriendInfo,
    
        // debug
        cmdTest: DebugChangeMeResponse,
    };
    
    module.exports = {
        LoginRequest: LoginRequest,
        LoginResponse: LoginResponse,
        LogoutRequest: LogoutRequest,
        LogoutResponse: LogoutResponse,
        SpinRequest: SpinRequest,
        SpinResponse: SpinResponse,
        BindFacebookRequest: BindFacebookRequest,
        BindFacebookResponse: BindFacebookResponse,
        RankRequest: RankRequest,
        RankResponse: RankResponse,
        HeartRequest: HeartRequest,
        HeartResponse: HeartResponse,
        ChatRequest: ChatRequest,
        ChatResponse: ChatResponse,
    
        // debug
        DebugChangeMeRequest: DebugChangeMeRequest,
        DebugChangeMeResponse: DebugChangeMeResponse,
    
        //push消息
        PushAttackedResponse: PushAttackedResponse,
        PushSendSpResponse: PushSendSpResponse,
        PushTakeSpResponse: PushTakeSpResponse,
        PushAddNewFriend: PushAddNewFriend,
        PushSyncFriendInfo: PushSyncFriendInfo,
    
        response_classes: response_classes
    };

    NetProxy.js

    /**
     * Created by skyxu on 2018/10/9.
     */
    
    "use strict";
    
    let GameNetwork = require("./GameNetwork");
    let GameProtocols = require("./GameProtocols");
    
    let GAME_SERVER_URL = 'ws://127.0.0.1:3000';
    // GAME_SERVER_URL = 'wss://echo.websocket.org';
    
    let NetProxy = cc.Class({
        ctor: function () {
            this.network = null;
            this._cachePushCallback = [];
        },
    
        init: function () {
            this.network = new GameNetwork();
            this.network.setDelegate(this);
            this.initPushCallback();
        },
    
        connect: function () {
            this.network.connect(GAME_SERVER_URL);
        },
    
        closeConnect: function () {
            this.network.closeConnect();
        },
    
        isNetworkOpened: function () {
            return this.network.isSocketOpened();
        },
    
        isNetworkClosed: function () {
            return this.network.isSocketClosed();
        },
    
        onNetworkOpen: function () {
            Global.eventMgr.emit(Global.config.EVENT_NETWORK_OPENED);
        },
    
        onNetworkClose: function () {
            Global.eventMgr.emit(Global.config.EVENT_NETWORK_CLOSED);
        },
    
        /**
         * 注册push回调接口
         */
        initPushCallback: function () {
            let self = this;
            let pushCallback = function (resp) {
                self.pushCallback(resp);
            };
    
            this.network.registerPushResponseCallback('chat', pushCallback);
    
    
            // let pushCallback = function(response){
            //     if(Util.DNN(farm.game) && farm.game.loginSuccess){
            //         this.dealCachePush();
            //         this.pushCallback(response);
            //     }else{
            //         this._cachePushCallback.push(response);
            //     }
            // }.bind(this);
    
            // this.network.registerPushResponseCallback('attacked', pushCallback);
            // this.network.registerPushResponseCallback('acceptWantHelp', pushCallback);
            // this.network.registerPushResponseCallback('sendSpNotify', pushCallback);
            // this.network.registerPushResponseCallback('takeSpNotify', pushCallback);
            // this.network.registerPushResponseCallback('wanted', pushCallback);
            // this.network.registerPushResponseCallback('incomplete', pushCallback);
            // this.network.registerPushResponseCallback('newFriend', pushCallback);
            // this.network.registerPushResponseCallback('news', pushCallback);
            // this.network.registerPushResponseCallback('hatredInfoSync', pushCallback);
            // this.network.registerPushResponseCallback('friendInfoSync', pushCallback);
        },
    
        /**
         * 处理缓存push
         */
        dealCachePush: function () {
            // if(this._cachePushCallback.length > 0){
            //     for(var i = 0; i < this._cachePushCallback.length; i++){
            //         this.pushCallback(this._cachePushCallback[i]);
            //     }
            // }
            // this._cachePushCallback = [];
        },
    
        beatHeart: function (callback) {
            let req = new GameProtocols.HeartRequest();
            req.t = Date.now();
            this.network.sendRequest(req, callback);
        },
    
        chat: function (msg) {
            let req = new GameProtocols.ChatRequest();
            let uid = cc.sys.localStorage.getItem("chat_uid");
            req.uid = uid;
            req.msg = msg;
            this.network.sendRequest(req);
        },
    
        /**
         * Facebook或者游客登录接口
         * @param {Object.<LoginOriginType>} origin
         * @param token
         */
        login: function (origin, token) {
            // let req = new GameProtocols.LoginRequest();
            // if(token) req.token = token;
            // req.origin = origin;
            // req.os = cc.sys.os;
            // req.osVersion = cc.sys.osVersion;
            // // req.language = cc.sys.language;//farm.FarmPlatformHelper.jsToOc(farm.FarmPlatformHelper.JSB_EVENT_JTO_GetCurrentLanguage);
            // /*
            //  req.deviceModel = '';
            //  req.channelId = 0;
            //  req.idfa = '';
            //  req.androidId = '';
            //  req.googleAid = '';
            //  req.appVersion = '';
            //  req.packName = '';
            //  */
            // let callback =  function (resp) {
            //     if(resp.err != 0){
            //         Global.eventMgr.emit(farm.game.gmConst.SP_EVENT_LOGIN_FAILED, resp);
            //         return;
            //     }
            //     if(resp.token && resp.token.length > 0){
            //         farm.localStorage.setItem(farm.game.gmConst.GLS_KEY_GUEST_TOKEN, resp.token);
            //     }
            //     farm.localStorage.removeItem(farm.game.gmConst.GLS_KEY_IS_LOGOUT);
            //
            //     //
            //     farm.game.initConfig(resp);
            //     farm.game.initData(resp);
            //     farm.game.loginSuccess = true;
            //
            //     // js 调取其他平台的sdk,传过去玩家id
            //     farm.FarmPlatformHelper.jsToOc(farm.FarmPlatformHelper.JSB_EVENT_JTO_setSessionWithUid, farm.game.player.id.toString());
            //     //
            //
            //     //登录
            //     farm.eventManager.emit(farm.game.gmConst.SP_EVENT_LOGIN_SUCCESS);
            // };
            // this.network.sendRequest(req, callback);
    
        },
    
        /**
         * Facebook或者游客登出
         */
        logout: function () {
            // let req = new GameProtocols.LogoutRequest();
            // this.network.sendRequest(req, function (resp) {
            //     if(resp.err != 0){
            //         cc.log("网络请求---LogoutRequest 失败");
            //         farm.eventManager.emit(farm.game.gmConst.SP_EVENT_LOGOUT_FAILED);
            //         return;
            //     }
            //     cc.log("网络请求---LogoutRequest 成功");
            //     Global.eventMgr.emit(farm.game.gmConst.SP_EVENT_LOGOUT_SUCCESS);
            // });
        },
    
        /**
         * 绑定fb账号
         * @param {String} token
         */
        bindFacebook: function (token) {
            // let req = new GameProtocols.BindFacebookRequest();
            // req.token = token;
            // let callback =  function (resp) {
            //     //绑定过得逻辑
            //     if(resp.err == farm.game.gmConst.ERROR_USER_HAS_REGISTERED){
            //         cc.log("网络请求---BindFacebookRequest 已绑定");
            //         farm.eventManager.emit(farm.game.gmConst.SP_EVENT_HAS_BIND_FACEBOOK);
            //         return;
            //     }
            //     //绑定失败
            //     if(resp.err != 0){
            //         cc.log("网络请求---BindFacebookRequest 失败");
            //         farm.eventManager.emit(farm.game.gmConst.SP_EVENT_BIND_FACEBOOK_FAILED);
            //         return;
            //     }
            //     //绑定成功
            //     cc.log("网络请求---BindFacebookRequest 成功");
            //     if(resp.me){
            //         farm.game.player.parse(resp.me);
            //     }
            //     if(resp.friends){
            //         farm.game.initFriends(resp.friends);
            //     }
            //     //绑定成功后删除本地token
            //     farm.localStorage.removeItem(farm.game.gmConst.GLS_KEY_GUEST_TOKEN);
            //     farm.eventManager.emit(farm.game.gmConst.SP_EVENT_BIND_FACEBOOK_SUCCESS);
            // };
            // this.network.sendRequest(req, callback);
        },
    
        /**
         * 启动转盘
         */
        spin: function (x) {
            // let req = new GameProtocols.SpinRequest();
            // if(farm.util.isNumber(x)){
            //     req.x = x;
            // }
            // var callback =  function (resp) {
            //     if(resp.err != 0){
            //         cc.log("网络请求---spin 失败");
            //         farm.eventManager.emit(farm.game.gmConst.SP_EVENT_SPIN_FAILED, resp);
            //         return;
            //     }
            //     cc.log("网络请求---spin 成功");
            //     farm.game.player.parse(resp.me);
            //     farm.game.spTimer.updateSpTime(resp.spStepLeftTime, resp.spInterval);
            //     farm.eventManager.emit(farm.game.gmConst.SP_EVENT_SPIN_SUCCESS, resp);
            // };
            // this.network.sendRequest(req, callback);
        },
    
        /**
         * 获取排名
         * @param {Number} rankType 0全部,1本地,2好友
         */
        getRank: function (rankType) {
            // let req = new GameProtocols.RankRequest();
            // req.type = rankType;
            // let callback =  function (resp) {
            //     if(resp.err != 0){
            //         cc.log("网络请求---getRank 失败");
            //         Global.eventMgr.emit(farm.game.gmConst.SP_EVENT_GET_RANK_FAILED, resp);
            //         return;
            //     }
            //     cc.log("网络请求---getRank 成功");
            //     // todo 暂定排名类型
            //     resp._rankType = rankType;
            //     //farm.game.initLeaderBoardArray(rankType, resp.myRank, resp.men);
            //     if(rankType == 2 && resp.men){
            //         farm.game.updateFriends(resp.men);
            //         resp.men = farm.game.sortFriendsByStar();
            //     }
            //     Global.eventMgr.emit(farm.game.gmConst.SP_EVENT_GET_RANK_SUCCESS, resp);
            // };
            // this.network.sendRequest(req, callback);
        },
    
    
        //push回调------------------------------------------------------------------------------
    
        /**
         * 推送回调
         */
        pushCallback: function (response) {
            switch (response.act){
                case "sendSpNotify":
                case "takeSpNotify":
                case "friendInfoSync":
                    this.pushFriendSendTakeSp(response);
                    break;
                case "stole":
                    this.pushStole(response);
                    break;
                case "attacked":
                    this.pushAttacked(response);
                    break;
                case "newFriend":
                    this.pushAddNewFriend(response);
                    break;
                case "chat":
                    this.pushChat(response);
                    break;
            }
        },
        /**
         * 好友间互赠体力推送
         * @param {PushSendSpResponse|PushTakeSpResponse} resp
         */
        pushFriendSendTakeSp: function (resp) {
            // cc.log("网络请求---push--- pushFriendSendTakeSp 成功");
            // if(resp.friend) farm.game.updateFriends(resp.friend);
            // farm.eventManager.emit(farm.game.gmConst.SP_PUSH_EVENT_UPDATE_FRIEND);
        },
    
        /**
         * 被偷
         * @param {PushStolenResponse} resp
         */
        pushStole: function (resp) {
            // cc.log("网络请求---push--- pushStole 成功");
            // if(resp.me) farm.game.player.parse(resp.me);
            // //if(resp.building) farm.game.buildings[resp.building.type].parse(resp.building);
            // if(resp.hatredman && !farm.game.getHelpWant(resp.hatredman.id)){
            //     farm.game.addEnemy(resp.hatredman);
            // }
            // if(resp.news){
            //     resp.news = farm.game.addNews(resp.news);
            // }
            // farm.eventManager.emit(farm.game.gmConst.SP_PUSH_EVENT_BE_STOLE_SUCCESS, resp);
        },
    
        /**
         * 被攻击
         * @param {PushAttackedResponse} resp
         */
        pushAttacked: function (resp) {
            // cc.log("网络请求---push--- pushAttacked 成功");
            // if(resp.me) {
            //     farm.game.player.parse(resp.me);
            //     farm.game.dataUpdater.updateStar();
            // }
            // if(resp.building) farm.game.buildings[resp.building.type].parse(resp.building);
            // if(resp.hatredman){
            //     farm.game.addBadass(resp.hatredman);
            //     if(!farm.game.getHelpWant(resp.hatredman.id)){
            //         farm.game.addEnemy(resp.hatredman);
            //     }
            // }
            // if(resp.news){
            //     resp.news = farm.game.addNews(resp.news);
            // }
            // farm.eventManager.emit(farm.game.gmConst.SP_PUSH_EVENT_BE_ATTACK_SUCCESS, resp);
        },
    
        /**
         * 新增好友
         * @param {PushAddNewFriend} resp
         */
        pushAddNewFriend: function (resp) {
            // cc.log("网络请求---push--- pushAddNewFriend 成功");
            // if(resp.friend){
            //     resp.friend = farm.game.addFriend(resp.friend);
            // }
            // if(resp.news){
            //     resp.news = farm.game.addNews(resp.news);
            // }
            // farm.eventManager.emit(farm.game.gmConst.SP_PUSH_EVENT_ADD_FRIEND_SUCCESS, resp);
        },
    
        pushChat: function (resp) {
            Global.eventMgr.emit(Global.config.EVENT_CHAT, resp);
        },
    
        /**
         * debug调试请求
         * @param {String} name
         */
        debug_addCoins: function (name) {
            var req = new GameProtocols.DebugChangeMeRequest();
            if (name === "btnAddCoins") {
                req.cmd = "player coins add 100000000";
            } else if (name === "btnClearCoins") {
                req.cmd = "player coins 0";
            } else if (name === "btnAddEnergy") {
                req.cmd = "player sp add 10";
            } else if (name === "btnClearEnergy") {
                req.cmd = "player sp 0";
            } else if (name == "btnAddWp") {
                req.cmd = "player wp add 10";
            } else if (name == "btnClearWp") {
                req.cmd = "player wp 0";
            } else if (name == "btnUnwrap"){
                req.cmd = "player fbuid null";
            } else if (name == "btnWizard1"){
                req.cmd = "player wizard1 0";
            } else if (name == "btnWizard2"){
                req.cmd = "player wizard2 0";
            } else if (name == "btnClearShield"){
                req.cmd = "player shield 0";
            } else if (name == "btnSpEc"){
                req.cmd = "SpEc stepInterval 60000";
            } else if (name == "btnFarmEc"){
                req.cmd = "FarmEc stepInterval 60000";
            } else if (name == "btnSpEcBack"){
                req.cmd = "SpEc stepInterval 3600000";
            } else if (name == "btnFarmBack"){
                req.cmd = "FarmEc stepInterval 86400000";
            } else if (name == "btnUpdateBuild"){
                req.cmd = "Building lv 5";
            } else {
                req.cmd = name;
            }
            // var callback = function (resp) {
            //     if (resp.err != 0) {
            //         return;
            //     }
            //     farm.game.player.parse(resp.me);
            //     farm.game.spTimer.updateSpTime(resp.spStepLeftTime, resp.spInterval);
            //     farm.game.dataUpdater.updateCoin();
            //     farm.game.dataUpdater.updateSp();
            //     farm.game.dataUpdater.updateShield();
            //     farm.game.dataUpdater.updateStar();
            //     //
            //     if((req.cmd == "FarmEc stepInterval 60000" || req.cmd == "FarmEc stepInterval 86400000")
            //         && farm.util.isNumber(resp.farmDailyOut)
            //         && farm.util.isNumber(resp.farmCoins)){
            //         farm.game.piggyBankTimer.init(resp.farmDailyOut, resp.farmCoins, resp.farmInterval);
            //     }
            //     if(req.cmd == "SpEc stepInterval 60000" || req.cmd == "SpEc stepInterval 3600000"){
            //         farm.game.spTimer.updateSpTime(resp.spStepLeftTime, resp.spInterval);
            //     }
            //     if(resp.buildings){
            //         for(var i = 0; i < resp.buildings.length; ++i){
            //             farm.game.buildings[i].parse(resp.buildings[i]);
            //         }
            //         farm.eventManager.emit(farm.game.gmConst.SP_EVENT_UPGRADE_BUILDING_SUCCESS, resp);
            //         farm.eventManager.emit(farm.game.gmConst.SP_DEBUG_EVENT_BUILD_TO_24_SUCCESS, resp);
            //     }
            // };
            // this.network.sendRequest(req, callback);
        },
    });
    
    module.exports = NetProxy;

    2、服务端

     使用express + express-ws组件,调用WSRouter的init初始化连接即可,

    let expressWS = require('express-ws');
    let work = require('./work');
    
    let wsRouter = null;
    
    function WSRouter(app, server){
        this.app = app;
        this.server = server;
        this.clients = [];
        expressWS(app, server);
    
        this.listenClientConnection = ()=>{
            app.ws('/', (socket, req)=>{
                console.log('client connect to server successful.');
                this.clients.push(socket);
                console.log('clients: ' + this.clients.length);
                socket.on('message', (msg)=>{
                    console.log('on message: ' + msg);
                    work(this, socket, msg);
                });
    
                socket.on('close', (msg)=>{
                    console.log('on close: ' + msg);
                    for (let index=0; index<this.clients.length; index++){
                        if (this.clients[index] == socket){
                            this.clients.splice(index,1);
                        }
                    }
                    console.log('clients: ' + this.clients.length);
                });
    
                socket.on('error', (err)=>{
                    console.log('on error: ' + error);
                });
            })
        }
    }
    
    
    module.exports = {
        init: function(app, server){
            if (wsRouter == null){
                wsRouter = new WSRouter(app, server);
            }
            return wsRouter;
        }
    }

     work.js

    module.exports = function(wsRouter, ws, msg){
        let msgObj = JSON.parse(msg);
        switch (msgObj.act){
            case 'heart':{
                msgObj.data.t = Date.now();
                ws.send(JSON.stringify(msgObj));
                break;
            }
            case 'chat': {
                for (let w of wsRouter.clients){
                    w.send(msg);
                }
                break;
            }
    
            default:
                break;
        }
    }
  • 相关阅读:
    防止浏览器记住用户名及密码的简单实用方法
    读懂CommonJS的模块加载
    gitHub上如何设置或者取消电子邮箱提醒
    echarts如何修改数据视图dataView中的样式
    vue中使用base64和md5
    java的特点
    java 运算符
    有关于分布式和SOA的理解
    WebService到底是什么
    并行计算、分布式计算和云计算的区别
  • 原文地址:https://www.cnblogs.com/skyxu123/p/9810741.html
Copyright © 2020-2023  润新知