使用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;
    }
}

 

posted @ 2018-10-18 15:25  随风的博客  阅读(6630)  评论(3编辑  收藏  举报