WebSocket基本使用和重连

333 阅读2分钟

基本实现

server端代码

const WebSocket = require('ws');
const port = 1024//端口

new WebSocket.Server({port}, function () {
    console.log('websocket服务开启')
}).on('connection', connectHandler)

function connectHandler (ws) {
    console.log('客户端连接')
    ws.on('error', errorHandler)
    ws.on('close', closeHandler)
    ws.on('message', messageHandler)

    setInterval(() => {
        ws.send('1234')
    }, 1000);

}

function messageHandler (e) {
    console.info('收到客户端信息')
}

function errorHandler (e) {
    console.info('客户端出错')
}

function closeHandler (e) {
    console.info('客户端已断开')
}


client代码

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>
        Title</title>
</head>
<body>
<script type="module">
    const name = 'test'//连接用户名
    let wsUrl = 'ws://127.0.0.1:1024/ws/?name=' + name
    let ws
    const init = () => {
        ws = new WebSocket(wsUrl)
        ws.onopen = function (e) {
            console.log('开启')
            ws.send(JSON.stringify({
                ModeCode: "message",
                msg: 'hello'
            }))
            setInterval(() => {
                ws.send(JSON.stringify({
                    ModeCode: "message",
                    msg: 'hello'
                }))
            }, 5000); // 模拟client持续向server发送消息
        }//连接上时回调
        ws.onmessage = function (e) {
            console.log('收到消息' + JSON.parse(e.data), new Date())
        }//收到服务端消息
        ws.onclose = function (e) {
            console.log('关闭')
        }//断开连接时回调
        ws.onerror = function (e) {
            console.log('出错')
        }//连接出错
    }
    init()
</script>
</body>
</html>

效果如下

server

image.png

client

image.png

重连

现在存在一个问题:当client因为某些原因(如网络失败等)断开长连接,应该想办法重新连接保证正常工作,重连可以使用定时器轮询查看是否正在工作,client代码:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>
        Title</title>
</head>
<body>
<script type="module">
    const name = 'test'//连接用户名
    let wsUrl = 'ws://127.0.0.1:1024/ws/?name=' + name
    let ws
    let isClosed = true; // 是否是关闭状态
    const init = () => {
        ws = new WebSocket(wsUrl)
        ws.onopen = function (e) {
            isClosed = false // 连接成功设置为false
            console.log('开启')
            ws.send(JSON.stringify({
                ModeCode: "message",
                msg: 'hello'
            }))
            setInterval(() => {
                ws.send(JSON.stringify({
                    ModeCode: "message",
                    msg: 'hello'
                }))
            }, 5000); // 模拟client持续向server发送消息
        }//连接上时回调
        ws.onmessage = function (e) {
            console.log('收到消息' + JSON.parse(e.data), new Date())
        }//收到服务端消息
        ws.onclose = function (e) {
            isClosed = true // 连接成功设置为true
            console.log('关闭')
        }//断开连接时回调
        ws.onerror = function (e) {
            isClosed = true // 连接成功设置为true
            console.log('出错')
        }//连接出错
    }
    setInterval(() => {
        if(isClosed) {
            console.log('尝试连接')
            init()
        }
    }, 3000) // 定时器,轮询是否需要连接
</script>
</body>
</html>

思路:设置定时器,通过isClosed字段判断是否需要重连。
效果如下:

image.png

存在问题

问题

上面代码看起来没问题(题主本来也看起来没问题,后续工作中发现存在问题,功能代码中依赖onmessage的方法在server返回一次的情况下重复调用)

image.png 在连接上的时候isClosed字段才设置为false(不是关闭状态),在连接不能及时返回(如网络问题)导致在下次定时器工作时还未连接,重复创建一遍WebSocket
有同学可能要说了(包括题主:D),ws是全局的,下次创建会覆盖,会被回收,但是真的是这样的吗?:

image.png
在初始阻塞(题主模拟了弱网状态)的情况下,创建了多个socket并且没有被销毁,持续调用onmessage

解决方法

  1. 方案一:把isClosed放到init方法中而不是回调中,这样就不会受网络阻塞等影响重复创建
  2. 方案二:不使用自己创建的变量,使用WebSocket.readyState状态判断是否需要连接,详见[MDN]