JS WebSocket类 - 包含[ 断线重连, 心跳检测, 粘包抑制 ]

JS WebSocket类 - 包含[ 断线重连, 心跳检测, 粘包抑制 ]

Tony哥
2018-09-13 / 0 评论 / 155 阅读 / 正在检测是否收录...

本方法是自己写的一个JS封装类,用于客户的机于服务器端的通讯,后端我用python完成的代码后期放出来,之前遇到过一个坑爹的问题就是客户端的粘包问题,原因是为了提高数据包效率同时发送的代码被粘在一起发送导致,目前无法解决,在网上找到了很多例子但是都不满足需要,有解决方案就是发送报头,后期如果解决会陆续跟进更新

/**
 * WebSocket类 - 包含[ 断线重连, 心跳检测, 粘包抑制 ]
 * @param __TF [true自动启动,默认false] 或 {
 *                                             host:str ws://地址:端口,
 *                                             name:str 客户端名称,
 *                                             onopen:fun 握手成功后触发,
 *                                             onclose:fun 关闭会话后触发,
 *                                             onmessage:fun 接收信息是触发,
 *                                             autoLink:bool 是否自动重连
 *                                             auto:bool 是否自动启动
 *                                            }
 * @returns {SKT}
 * @constructor {
 *                  onopen:fun 握手成功后触发,
 *                  onclose:fun 关闭会话后触发,
 *                  onmessage:fun 接收信息是触发,
 *                  start:fun 开始握手,
 *                  send:fun(str) 发送信息,
 *                  close|stop:fun 停止方法,
 *                  host:str ws://地址:端口,
 *                  name:str 客户端名称,
 *                  autoLink:bool 是否自动重连,默认true
 *                 }
 */
var WSK=function(__TF){
    this.host="ws://127.0.0.1";this.socket=null;this.name='';
    this.autoLink=!0; //--允许断线重连
    this.onopen = function(s){if(typeof s == 'function')this.onopen=s;};
    this.onmessage = function(msg){if(typeof msg == 'function'){this.onmessage=msg;}};
    this.onclose = function(s){if(typeof s == 'function'){this.onclose=s;}};
    /**
     * 开启一个握手会话
     */
    this.start=function() {
        try {
            var that=this;
            this.socket = new WebSocket(this.host);
            this.socket.onopen = function () {
                that.__autoLink_num=0;
                clearTimeout(this.__autoLink_t);
                that.socket.send('__reg__:' + that.name);
                that.__hart();//--定时心跳----
                that.onopen();
            };
            this.socket.onmessage = function (e) {
                if(e.data == '__quit__'){
                    that.close(); return !0;
                }
                var s = that.onmessage(e.data);
                if (s === undefined  || s === null) return !0;
                that.send(s);
            };
            this.socket.onclose = function () {
                that.__autoLink(); //--握手尝试器
            };
        }
        catch (ex) {
            this.__autoLink();
        }
    };
    /**
     * 关闭通讯
     * @type {WSK.stop}
     */
    this.close = this.stop = function () {
        clearTimeout(this.__autoLink_t);
        this.autoLink=!1; //告诉系统不用重试了本操作是人工或服务端停止的
        this.__tmp=[];    //清空排队阵列
        try {this.socket.close();}catch (ex) {}
    };
    /**
     * 发送数据到服务端 引入排队机制规避粘包问题
     * @param msg 要发送的信息
     * @returns {boolean}
     */
    this.send = function (msg) {
        if (msg === undefined  || msg === null) return !1;
        var that=this;
        try{
            if(typeof msg == "boolean") msg=msg?'!0':'!1';//--布尔类型就转换一下形式
            else msg=JSON.stringify(msg);
        }catch(e){msg=msg.toString();}
        this.__tmp.push(msg);
        if(that.__t === undefined) {//--阻止粘包事件策略------------
            this.__t = setInterval(function () {
                if (that.socket.bufferedAmount == 0) //--如果缓冲区在忙碌及等定时器下一轮
                    if (that.__tmp.length) {
                        try{
                            that.socket.send(that.__tmp[0]);
                            
                        }catch (e) {}that.__tmp.splice(0, 1);

                    } else {
                        clearInterval(that.__t);
                        that.__t = undefined;
                    }
            }, 5);
        }
    };
    this.__tmp=[];//--排队缓存
    /**
     * 定时器心跳
     * @param T
     * @returns {boolean}
     */
    this.__hart=function(T){
        if(socket.readyState==2 || socket.readyState==3) return !1;
        if(T) socket.send('__reg__'); //--心跳
        setTimeout(function () {
            this.__hart(1);//log('心跳')
        },5000);
    };
    /**
     * 自动重连内置方法
     * @private
     */
    this.__autoLink_t={};//--重试定时器
    this.__autoLink_num=0;//--重复计数器--
    this.__autoLink = function () {
        if(this.autoLink){
            var that = this;
            this.__al_time=this.__al_time?this.__al_time:1000;
            this.__autoLink_t=setTimeout(function () {
                that.__autoLink_num++;
                if(that.__autoLink_num>2){
                    that.__al_time = 5000;
                }
                if(that.__autoLink_num>10){
                    console.log('放弃尝试....');
                    that.autoLink=!1;
                    that.onclose();
                    return !1;
                }
                console.log('['+that.__autoLink_num+'] 尝试重连...');
                that.start();
            },this.__al_time);
        }else this.onclose();
    };
    if(typeof __TF == 'object' && __TF){ //--传递参数--赋值
        if(__TF.hasOwnProperty('host'))this.host=__TF['host'];
        if(__TF.hasOwnProperty('name'))this.name=__TF['name'];
        if(__TF.hasOwnProperty('onopen') && typeof __TF['onopen'] == 'function')this.onopen(__TF['onopen']);
        if(__TF.hasOwnProperty('onclose') && typeof __TF['onclose'] == 'function')this.onclose(__TF['onclose']);
        if(__TF.hasOwnProperty('onmessage') && typeof __TF['onmessage'] == 'function')this.onmessage(__TF['onmessage']);
        if(__TF.hasOwnProperty('autoLink'))this.autoLink=__TF['autoLink']?true:false;
        if(__TF.hasOwnProperty('auto') && __TF['auto'])this.start();
    }else if(__TF === true) this.start();
    return this;
};

调用实例:

/**---配置参数目录----------------------*/
        window.o=WSK({
            host:"ws://服务器地址:端口",
            name:'客户端名称'
        });
        o.onopen(function(){
            console.log('成功握手')
        });
        o.onmessage(function(msg){
            console.log(msg)
        });
        o.onclose(function(){
            console.log('连接断开')
        });

        $("#start").click(function () {
            o.start();
        });
        $("#stop").click(function () {
            o.stop();
        });

服务器端,命令参考,功能需要您自行编写:

接收来自客户端的命令:
 __reg__:注册名称  服务器端先握手再接收注册,指定时间内不注册断开握手(看作登录吧)
 __reg__ 心跳检测  服务器端接收心跳,如果心跳不存在就断开客户端连接
0

评论 (0)

取消