websocket超时重连、心跳检测

websocket

在单个TCP连接上进行全双工通信的协议,可以实现服务端和客户端双向推送信息的协议。我们在使用webscoket通信时必须要注意的问题超时重连和心跳检测。
超时重连:当出现错误时客户端尝试重新连接websocket。
心跳检测:客户端长时间没接收到服务端消息,就向服务端发送请求,查看服务端是否还在,如果服务端在规定时间未回复消息则表明服务端由于某种原因中断了,那么客户端也就可以中断连接了。当然下面代码可以通过reconnect再次选择重连。

interface WsOptions {
    url: string
    reconnectCountLimit?: number
    reconnectInterval?: number
    heartDeadline?: number
    watch?: boolean
}

class WebSocketClient {
    #ws: WebSocket | undefined
    #needReconnect: boolean
    #count: number
    url: string
    reconnectCountLimit: number
    watch: boolean
    reconnectTime: NodeJS.Timeout | null
    reconnectInterval: number
    lastMessageTime: number
    heartDeadline: number
    heartCloseTime: NodeJS.Timeout | null
    checkHeartTime: NodeJS.Timeout | null
    receiveData: {
        type: string
        data: any
    }[]

    constructor(options: WsOptions) {
        this.url = options.url
        this.#needReconnect = false//是否需要重连
        this.watch = options.watch || true//是否开启心跳
        this.reconnectCountLimit = options.reconnectCountLimit || 3 //重连次数
        this.#count = 0 //重连次数计数
        this.reconnectTime = null//重连计时器
        this.reconnectInterval = options.reconnectInterval || 5000//重连时间间隔
        this.lastMessageTime = 0 //记录上次收到信息的时间 ->暂时没使用
        this.heartDeadline = options.heartDeadline || 5000//心跳检测截止时间 每次收到信息后将在该时间后发送心跳消息,在该时间内未收到信息就终端ws连接
        this.heartCloseTime = null //心跳关闭计时器
        this.checkHeartTime = null  //发送心跳消息计时器
        this.#initWS()//启动ws
        this.watch && this.heartBeat()//启动心跳检测
        this.receiveData = []
    }

    get websocket() {
        return this.#ws
    }

    //初始化websocket
    #initWS() {
        this.#ws = new WebSocket(this.url)
        this.#ws.addEventListener('open', () => {
            this.initState()
        })
        this.#ws.addEventListener('close', () => {
            console.log('链接关闭')
        })
        this.#ws.addEventListener('error', () => {
            this.#needReconnect = true
            this.reconnect()
        })
        this.#ws.addEventListener('message', (e) => {
            this.lastMessageTime = Date.now()
            try {
                const data = JSON.parse(e.data)
                this.receiveData = this.receiveData.map(item => {
                    if (item.type === data.type) {
                        item.data = data.data
                    }
                    return item
                })
            } catch (e) {
                console.log("无法解析")
            }
            this.closeCheckHeart()
            this.watch && this.heartBeat()//启动心跳检测
        })
    }

    //重连
    reconnect() {
        if (this.#needReconnect && this.#count < this.reconnectCountLimit) {
            this.reconnectTime = setTimeout(() => {
                this.#count++
                this.#initWS()
            }, this.reconnectInterval);
            return
        }
        this.initState()
    }

    //初始化数据
    initState() {
        this.#needReconnect = false
        this.#count = 0
        this.reconnectTime && clearInterval(this.reconnectTime)
    }

    closeCheckHeart() {
        this.checkHeartTime = null
        this.heartCloseTime = null
        clearTimeout(this.checkHeartTime!)
        clearTimeout(this.heartCloseTime!)
    }

    send(data: any) {
        const websocketStatus: Record<number, () => void> = {
            0: () => {
                console.warn("ws连接中")
            },
            1: () => {
                this.#ws!.send(data)
            },
            3: () => {
                this.#initWS()
            },
            2: () => {
                console.warn("ws连接正在关闭")
            }
        }
        websocketStatus[this.#ws!.readyState]()
    }

    //心跳检测
    heartBeat() {
        this.checkHeartTime = setTimeout(() => {
            this.send(JSON.stringify({
                type: 'heart'
            }))
            console.log("check websocket status")
        }, this.heartDeadline)
        this.heartCloseTime = setTimeout(() => {
            if (Date.now() - this.lastMessageTime > this.heartDeadline * 2) {
                this.#ws!.close()
                this.closeCheckHeart()
            }
        }, this.heartDeadline * 2)
    }
}

export const ws = new WebSocketClient({url: import.meta.env.VITE_WS_URL})





import { WebSocketServer } from 'ws';
const wss = new WebSocketServer({ port: 8888 });

wss.on('connection', function connection(ws) {
  ws.on('message', function message(data) {
    const res = data.toString()
    console.log(res)
    if (res.startsWith('{') && JSON.parse(res)?.type === 'heart') {
      // ws.send('{heart:exist}')
    }
  });
  wss.clients.forEach(client => {
    client && client.send('广播信息')
  })
  ws.send('I am service');
  setTimeout(() => {
    ws.send('xxx');
  },10000)
});
wss.onopen = function (e) {
  console.log('服务端链接');
}
  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

大鲤余

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值