最近被问到一个项目中比较常见的问题,因为之前自己只是用过,但并未真正了解具体是怎么实现的,所以今天抽时间整理了下,避免下次再掉坑中。。

一般toB的项目中,如果有实时通信的需求,目前较为广泛的都会采用websocket来实现客户端与服务端的双向通信,其实主要原理是客户端定时向服务端发送消息,如果在规定时间内,收到服务端推送回的消息,则说明通信正常,否则就挂掉了,及时关闭连接;

今天简单封装了下,websocket中的心跳重连,主要代码如下:

class HeartSocket {

    constructor(options) {
        this.url = options.url; //请求路径
        this.timeout = options.timeout;//心跳超时时间 毫秒
        this.isHeart = options.isHeart;//是否开启心跳
        this.isReconnection = options.isReconnection; //是否断开重连
        this.ws = null; ///WebSocket的引用
        this.status = null;//记录状态
        this.lockReconnect = false,//避免重复连接
        this.timeoutObj = null;//模拟延时器1
        this.serverTimeoutObj = null;//模拟延时器2
    }

    reConnection() {
        if (this.lockReconnect) return;
        this.lockReconnect = true;
        //判断当前浏览器是否支持WebSocket
        if ('WebSocket' in window) {
            this.ws = new WebSocket(this.url);
        } else {
            alert('Not support websocket')
        }

        //打开连接
        this.ws.onopen = (e) => {
            this.status = 'open';
            console.log('socket连接成功', e)
            if (this.isHeart) this.heartCheck();
        }

        //消息交互
        this.ws.onmessage = (e) => {
            console.log('后台接收到前台传值,还有心跳...');
            //心跳检测重置
            this.heartCheck();
        }

        //关闭连接
        this.ws.onclose = (e) => {
            console.log('关闭连接', e);
            this.closeSocket(e);
        }

        //报错
        this.ws.onerror = (e) => {
            console.log('error', e);
            this.closeSocket(e);
        }
    }

    resetHeart() {
        this.timeoutObj && clearTimeout(this.timeoutObj);
        this.serverTimeoutObj && clearTimeout(this.serverTimeoutObj);
    }

    heartCheck() {
        console.log('开始测试心跳');
        this.timeoutObj = setTimeout(() => {
            //这里发送一个心跳,后端收到后,返回一个心跳消息,
            console.log('发送消息,测试后台是否运行中...');
            //任意发一个消息过去,后台接收,如果在onmessage收到消息,说明后台没有挂掉,有心跳
            this.ws.send("666");
            this.serverTimeoutObj = setTimeout(() => {
                console.log("后台挂掉,没有心跳了....");
                console.log("打印websocket的地址:" + this.url);
                this.ws.close();
                // createWebSocket();
            }, this.timeout);
            this.lockReconnect = false;
        }, this.timeout);
    }

    closeSocket(e) {
        this.resetHeart();
        if (this.status !== 'close') {
            console.log('断开,重连', e)
            if (this.isReconnection) {
                console.log("正在重连,当前时间" + new Date())
                //重连
                this.reConnection();
            }
        } else {
            //手动关闭
            console.log('手动关闭', e)
        }
    }

    close() {
        this.status = 'close';
        this.resetHeart();
        return this.ws.close();
    }

    sendMsg(data) {
        let msg = JSON.stringify(data);
        return this.ws.send(msg)
    }

}

调用如下:

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>websocket心跳重连</title>
</head>

<body>
    <div>
        <input type="text" placeholder="请输入路径" value="ws://172.16.253.166:48081/websocket" id="url">
        <button onclick="concat()"> concat </button>
        <button onclick="closeWebSocket()"> close </button>
    </div>
    <script src="./index.js"></script>
    <script>

        let url = document.getElementById('url').value;

        const ws = new HeartSocket({
            url: url,
            isHeart: true, //是否心跳
            timeout: 10000,// 心跳间隔时间
            isReconnection: true, //是否断开后自动重连
        })

        function concat() {
            //判断客户端是否联网
            // if (navigator.onLine) {
            //     console.log('网络已连接');
            // } else {
            //     console.log('已断网');
            // }
            ws.reConnection();
        }

        // 发送消息
        //ws.sendMsg(message);

        //关闭连接
        function closeWebSocket() {
            if (ws) ws.close();
        }

        //监听窗口关闭事件,当窗口关闭时,主动去关闭websocket连接,防止连接还没断开就关闭窗口,server端会抛异常。
        window.onbeforeunload = function () {
            ws.close();
        }

    </script>
</body>

</html>

如有问题,还请及时指正,蟹蟹~