由于web/app有时候会出现网络不稳定websocket会自动断开连接导致消息推送不了的情况,所以查阅资料后发现了一个心跳机制,也就是客户端间隔一段时间就向服务器发送一条消息,如果服务器收到消息就回复一条信息过来,如果一定时间内没有回复,则表示已经与服务器断开连接了,这个时候就需要进行重连。

      首先在vue里使用:

var lockReconnect = false;  //避免ws重复连接
var ws = null;          // 判断当前浏览器是否支持WebSocket
var wsUrl = serverConfig.socketUrl;
createWebSocket(wsUrl);   //连接ws

function createWebSocket(url) {
    try{
        if('WebSocket' in window){
            ws = new WebSocket(url);
        }
        initEventHandle();
    }catch(e){
        reconnect(url);
        console.log(e);
    }     
}

function initEventHandle() {
    ws.onclose = function () {
        reconnect(wsUrl);
        console.log("llws连接关闭!"+new Date().toLocaleString());
    };
    ws.onerror = function () {
        reconnect(wsUrl);
        console.log("llws连接错误!");
    };
    ws.onopen = function () {
        heartCheck.reset().start();      //心跳检测重置
        console.log("llws连接成功!"+new Date().toLocaleString());
    };
    ws.onmessage = function (event) {    //如果获取到消息,心跳检测重置
        heartCheck.reset().start();      //拿到任何消息都说明当前连接是正常的
        console.log("llws收到消息啦:" +event.data);
        if(event.data!='pong'){
            let data = JSON.parse(event.data);
        }
    };
}
// 监听窗口关闭事件,当窗口关闭时,主动去关闭websocket连接,防止连接还没断开就关闭窗口,server端会抛异常。
window.onbeforeunload = function() {
    ws.close();
}  

function reconnect(url) {
    if(lockReconnect) return;
    lockReconnect = true;
    setTimeout(function () {     //没连接上会一直重连,设置延迟避免请求过多
        createWebSocket(url);
        lockReconnect = false;
    }, 2000);
}

//心跳检测
var heartCheck = {
    timeout: 600000,        //10分钟发一次心跳
    timeoutObj: null,
    serverTimeoutObj: null,
    reset: function(){
        clearTimeout(this.timeoutObj);
        clearTimeout(this.serverTimeoutObj);
        return this;
    },
    start: function(){
        var self = this;
        this.timeoutObj = setTimeout(function(){
            //这里发送一个心跳,后端收到后,返回一个心跳消息,
            //onmessage拿到返回的心跳就说明连接正常
            ws.send("ping");
            console.log("ping!")
            self.serverTimeoutObj = setTimeout(function(){//如果超过一定时间还没重置,说明后端主动断开了
                ws.close();     //如果onclose会执行reconnect,我们执行ws.close()就行了.如果直接执行reconnect 会触发onclose导致重连两次
            }, self.timeout)
        }, this.timeout)
    }
}

在uni-app中使用:两个差别不大,只有new websocket的方法不一样:

wsObj = uni.connectSocket({
			url: url,
			success: () => {
				console.log('WebSocket 连接成功');
			},
			fail: (err) => {
				console.log("uni connect socket err: ", err);
			}
		});

遇到的坑一:

不知道大家有没有注意每次重连的时候,我们都是重新new一个websocket,下面是正常新建websocket的对象,readyState为1,但是由于我的需求是要在不同的layout里使用websocket,所以没有每次new一遍,这样会导致页面刷新并且退出登录,所以我做了一个判断,如果有websocket并且readState不为3就不用再new一次,并且见下面代码,至于为什么会用到readState,等下来说

function createWebSocket (enterWsHost) {
    wsHost = enterWsHost
    console.log('在连接中吗',wsHost)
    try {
      if ('WebSocket' in window) {
        if (ws && ws.readyState !== 3) {
          console.log('用之前的ws')
          return ws
        } else {
          console.log('重新创建ws')
          ws = new WebSocket(wsHost)
        }

        initEvent()
      }

    } catch (error) {
      console.log('失败')
      // 重连
      reconnect(wsHost)
    }
  }

为什么必要要判断readState呢,首先来看看它的所有状态码,它表示当前连接的状态:3表示连接已经关闭或者没有连接成功,所以当有这种情况只有重新new一次。

0 (WebSocket.CONNECTING)
正在链接中
1 (WebSocket.OPEN)
已经链接并且可以通讯
2 (WebSocket.CLOSING)
连接正在关闭
3 (WebSocket.CLOSED)
连接已关闭或者没有链接成功

遇到的坑二:

websocket的onopen / onmessage / onerror / onclose四个属性会有一个回调函数,之前用的时候完全没有注意先后顺序,直接随便写的,但是发现会出现收不到服务器的消息情况,后来检查了很久才发现顺序错了。

WebSocket.onopen属性定义一个事件处理程序,当WebSocket 的连接状态readyState 变为1时调用;这意味着当前连接已经准备好发送和接受数据。

WebSocket.onmessage 属性是一个当收到来自服务器的消息时被调用。

这两个调用的顺序,必须是onopen在前面,onmessage在后面,不然就会出现接收不到消息的情况

 

Logo

为开发者提供学习成长、分享交流、生态实践、资源工具等服务,帮助开发者快速成长。

更多推荐