WebSocket在2008年被提出,其通信协议于2011被制定为标准
与http不同,websocket支持全双工通信(即:在客户端和服务之间双向通信)在websocket问世之前,客户端与服务器通常采用http轮询和Comet等方式保持长链接
然而,这么做无疑会对服务端造成资源消耗,因为HTTP请求包含较长的头文件,只传递了少许的有用信息,十分消耗资源。
于是websocket便诞生了,它不仅节省资源和带宽,更是能实现长链接作用,只需客户端主动与服务端握手一次,即可进行实时通信,实现推送技术。

之前我也写过相关的文章:Socket聊天室使用JS+socket.io+WebRTC+nodejs+express搭建一个简易版远程视频聊天,但是用到的模块都是socket.io,而且没有深入优化,在平时工作上真正用到时发现事情并不简单。有时前端或者后端会断线而对方不知道,像弱网或者后端服务器重启时,前端并不能保证一直连接
所以这篇文章,我们就来使用websocket做一个简单的demo,并且加上心跳和断线重连功能

首先是服务端,采用node+ws模块搭建websocket服务,在server文件夹下新建server.js,并在npm初始化后,下载ws模块

npm init -y
npm i ws

引入ws模块,并搭建一个简单的websocket服务

const WebSocket = require('ws');
const port = 1024//端口
const pathname = '/ws/'//访问路径

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)
}

function messageHandler (e) {
console.info(‘接收客户端消息’)
this.send(e)
}

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

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

前端部分也搭建个ws访问客户端

<!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
    const ws = new WebSocket(wsUrl)
    ws.onopen = function (e) {
        console.log('开启')
        ws.send(JSON.stringify({
            ModeCode: "message",
            msg: 'hello'
        }))
    }//连接上时回调
    ws.onclose = function (e) {
        console.log('关闭')
    }//断开连接时回调
    ws.onmessage = function (e) {
        let data = JSON.parse(e.data)
        console.log('收到消息' + data.msg)
        ws.close()
    }//收到服务端消息
    ws.onerror = function (e) {
        console.log('出错')
    }//连接出错
</script>
</body>
</html>

前端打印结果:

服务端打印结果:

有以上效果说明一个最简单的ws连接就实现了,下面,我们优化一下,为了降低耦合,我们先引入eventBus发布订阅,然后新建一个websocket类继承自原生WebSocket,因为,我们要在里面做心跳和重连
在服务端,我们先把server完善一下,通过http的upgrade过滤验证ws连接
在原有的服务端增加http服务并做好路径验证

const http = require('http');
const server = http.createServer()

server.on(“upgrade”, (req, socket, head) => {//通过http.server过滤数据
let url = new URL(req.url, http://<span class="hljs-subst">${req.headers.host}</span>)
let name = url.searchParams.get(‘name’)//获取连接标识
if(!checkUrl(url.pathname, pathname)) {//未按标准
socket.write(‘未按照标准访问’);
socket.destroy();
return;
}
})
server.listen(port, () => {
console.log(‘服务开启’)
})

//验证url标准
function checkUrl (url, key) {//判断url是否包含key
return - ~ url.indexOf(key)
}

完成httpServer后,我们再完善一下websocket服务,将每一个连接的用户都通过代理保存并实现增删,得到以下完整的服务端

const http = require('http');
const WebSocket = require('ws');
const port = 1024//端口
const pathname = '/ws/'//访问路径
const server = http.createServer()

class WebSocketServer extends WebSocket.Server {
constructor () {
super(…arguments);
this.webSocketClient = {}//存放已连接的客户端
}

set ws (val) {<span class="hljs-comment">//代理当前的ws,赋值时将其初始化</span>
    <span class="hljs-variable language_">this</span>.<span class="hljs-property">_ws</span> = val
    val.<span class="hljs-property">t</span> = <span class="hljs-variable language_">this</span>;
    val.<span class="hljs-title function_">on</span>(<span class="hljs-string">'error'</span>, <span class="hljs-variable language_">this</span>.<span class="hljs-property">errorHandler</span>)
    val.<span class="hljs-title function_">on</span>(<span class="hljs-string">'close'</span>, <span class="hljs-variable language_">this</span>.<span class="hljs-property">closeHandler</span>)
    val.<span class="hljs-title function_">on</span>(<span class="hljs-string">'message'</span>, <span class="hljs-variable language_">this</span>.<span class="hljs-property">messageHandler</span>)
}

get ws () {
    <span class="hljs-keyword">return</span> <span class="hljs-variable language_">this</span>.<span class="hljs-property">_ws</span>
}

messageHandler (e) {
    <span class="hljs-variable language_">console</span>.<span class="hljs-title function_">info</span>(<span class="hljs-string">'接收客户端消息'</span>)
    <span class="hljs-keyword">let</span> data = <span class="hljs-title class_">JSON</span>.<span class="hljs-title function_">parse</span>(e)
    <span class="hljs-keyword">switch</span>(data.<span class="hljs-property">ModeCode</span>) {
        <span class="hljs-keyword">case</span> <span class="hljs-string">'message'</span>:
            <span class="hljs-variable language_">console</span>.<span class="hljs-title function_">log</span>(<span class="hljs-string">'收到消息'</span> + data.<span class="hljs-property">msg</span>)
            <span class="hljs-variable language_">this</span>.<span class="hljs-title function_">send</span>(e)
            <span class="hljs-keyword">break</span>;
        <span class="hljs-keyword">case</span> <span class="hljs-string">'heart_beat'</span>:
            <span class="hljs-variable language_">console</span>.<span class="hljs-title function_">log</span>(<span class="hljs-string">`收到<span class="hljs-subst">${<span class="hljs-variable language_">this</span>.name}</span>心跳<span class="hljs-subst">${data.msg}</span>`</span>)
            <span class="hljs-variable language_">this</span>.<span class="hljs-title function_">send</span>(e)
            <span class="hljs-keyword">break</span>;
    }
}

errorHandler (e) {
    <span class="hljs-variable language_">this</span>.<span class="hljs-property">t</span>.<span class="hljs-title function_">removeClient</span>(<span class="hljs-variable language_">this</span>)
    <span class="hljs-variable language_">console</span>.<span class="hljs-title function_">info</span>(<span class="hljs-string">'客户端出错'</span>)
}

closeHandler (e) {
    <span class="hljs-variable language_">this</span>.<span class="hljs-property">t</span>.<span class="hljs-title function_">removeClient</span>(<span class="hljs-variable language_">this</span>)
    <span class="hljs-variable language_">console</span>.<span class="hljs-title function_">info</span>(<span class="hljs-string">'客户端已断开'</span>)
}

addClient (item) {<span class="hljs-comment">//设备上线时添加到客户端列表</span>
    <span class="hljs-keyword">if</span>(<span class="hljs-variable language_">this</span>.<span class="hljs-property">webSocketClient</span>[item[<span class="hljs-string">'name'</span>]]) {
        <span class="hljs-variable language_">console</span>.<span class="hljs-title function_">log</span>(item[<span class="hljs-string">'name'</span>] + <span class="hljs-string">'客户端已存在'</span>)
        <span class="hljs-variable language_">this</span>.<span class="hljs-property">webSocketClient</span>[item[<span class="hljs-string">'name'</span>]].<span class="hljs-title function_">close</span>()
    }
    <span class="hljs-variable language_">console</span>.<span class="hljs-title function_">log</span>(item[<span class="hljs-string">'name'</span>] + <span class="hljs-string">'客户端已添加'</span>)
    <span class="hljs-variable language_">this</span>.<span class="hljs-property">webSocketClient</span>[item[<span class="hljs-string">'name'</span>]] = item
}

removeClient (item) {<span class="hljs-comment">//设备断线时从客户端列表删除</span>
    <span class="hljs-keyword">if</span>(!<span class="hljs-variable language_">this</span>.<span class="hljs-property">webSocketClient</span>[item[<span class="hljs-string">'name'</span>]]) {
        <span class="hljs-variable language_">console</span>.<span class="hljs-title function_">log</span>(item[<span class="hljs-string">'name'</span>] + <span class="hljs-string">'客户端不存在'</span>)
        <span class="hljs-keyword">return</span>;
    }
    <span class="hljs-variable language_">console</span>.<span class="hljs-title function_">log</span>(item[<span class="hljs-string">'name'</span>] + <span class="hljs-string">'客户端已移除'</span>)
    <span class="hljs-variable language_">this</span>.<span class="hljs-property">webSocketClient</span>[item[<span class="hljs-string">'name'</span>]] = <span class="hljs-literal">null</span>
}

}

const webSocketServer = new WebSocketServer({noServer: true})
server.on(“upgrade”, (req, socket, head) => {//通过http.server过滤数据
let url = new URL(req.url, http://<span class="hljs-subst">${req.headers.host}</span>)
let name = url.searchParams.get(‘name’)//获取连接标识
if(!checkUrl(url.pathname, pathname)) {//未按标准
socket.write(‘未按照标准访问’);
socket.destroy();
return;
}
webSocketServer.handleUpgrade(req, socket, head, function (ws) {
ws.name = name//添加索引,方便在客户端列表查询某个socket连接
webSocketServer.addClient(ws);
webSocketServer.ws = ws
});
})
server.listen(port, () => {
console.log(‘服务开启’)
})

//验证url标准
function checkUrl (url, key) {//判断url是否包含key
return - ~ url.indexOf(key)
}

当连接断开时,只有客户端主动访问服务端才能实现重连,所以客户端的功能要比服务端更多一些,我们把客户端的websocket完善优化一下,添加一些简单的控制功能(连接,发消息,断开)的按钮,这里有一点需要注意:在下次连接之前一定要先关闭当前连接,否则会导致多个客户端同时连接,消耗性能

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>
        Title</title>
</head>
<body>
<button id="connect">
    连接
</button>
<button disabled
        id="sendMessage">
    发送
</button>
<button disabled
        id="destroy">
    关闭
</button>
<script type="module">
    const name = 'test'//连接用户名
    let connect = document.querySelector('#connect'),//连接按钮
        sendMessage = document.querySelector('#sendMessage'),//发送按钮
        destroy = document.querySelector('#destroy'),//关闭按钮
        wsUrl = 'ws://127.0.0.1:1024/ws/?name=' + name,//连接地址
        ws;
connect.<span class="hljs-title function_">addEventListener</span>(<span class="hljs-string">'click'</span>, connectWebSocket)
sendMessage.<span class="hljs-title function_">addEventListener</span>(<span class="hljs-string">'click'</span>, <span class="hljs-keyword">function</span> (<span class="hljs-params">e</span>) {
    ws.<span class="hljs-title function_">send</span>(<span class="hljs-title class_">JSON</span>.<span class="hljs-title function_">stringify</span>({
        <span class="hljs-title class_">ModeCode</span>: <span class="hljs-string">"message"</span>,
        <span class="hljs-attr">msg</span>: <span class="hljs-string">'hello'</span>
    }))
})
destroy.<span class="hljs-title function_">addEventListener</span>(<span class="hljs-string">'click'</span>, <span class="hljs-keyword">function</span> (<span class="hljs-params">e</span>) {
    ws.<span class="hljs-title function_">close</span>()
    ws = <span class="hljs-literal">null</span>
})

<span class="hljs-keyword">function</span> <span class="hljs-title function_">connectWebSocket</span> () {
    <span class="hljs-keyword">if</span>(!ws) {<span class="hljs-comment">//第一次执行,初始化或ws断开时可执行</span>
        ws = <span class="hljs-keyword">new</span> <span class="hljs-title class_">WebSocket</span>(wsUrl)
        <span class="hljs-title function_">initWebSocket</span>()
    }
}

<span class="hljs-keyword">function</span> <span class="hljs-title function_">initWebSocket</span> () {
    ws.<span class="hljs-property">onopen</span> = <span class="hljs-keyword">function</span> (<span class="hljs-params">e</span>) {
        <span class="hljs-title function_">setButtonState</span>(<span class="hljs-string">'open'</span>)
        <span class="hljs-variable language_">console</span>.<span class="hljs-title function_">log</span>(<span class="hljs-string">'开启'</span>)
    }<span class="hljs-comment">//连接上时回调</span>
    ws.<span class="hljs-property">onclose</span> = <span class="hljs-keyword">function</span> (<span class="hljs-params">e</span>) {
        <span class="hljs-title function_">setButtonState</span>(<span class="hljs-string">'close'</span>)
        <span class="hljs-variable language_">console</span>.<span class="hljs-title function_">log</span>(<span class="hljs-string">'关闭'</span>)
    }<span class="hljs-comment">//断开连接时回调</span>
    ws.<span class="hljs-property">onmessage</span> = <span class="hljs-keyword">function</span> (<span class="hljs-params">e</span>) {
        <span class="hljs-keyword">let</span> data = <span class="hljs-title class_">JSON</span>.<span class="hljs-title function_">parse</span>(e.<span class="hljs-property">data</span>)
        <span class="hljs-variable language_">console</span>.<span class="hljs-title function_">log</span>(<span class="hljs-string">'收到消息'</span> + data.<span class="hljs-property">msg</span>)
    }<span class="hljs-comment">//收到服务端消息</span>
    ws.<span class="hljs-property">onerror</span> = <span class="hljs-keyword">function</span> (<span class="hljs-params">e</span>) {
        <span class="hljs-title function_">setButtonState</span>(<span class="hljs-string">'close'</span>)
        <span class="hljs-variable language_">console</span>.<span class="hljs-title function_">log</span>(<span class="hljs-string">'出错'</span>)
    }<span class="hljs-comment">//连接出错</span>
}

<span class="hljs-comment">/*
  • 设置按钮是否可点击
  • @param state:open表示开启状态,close表示关闭状态
    */
    function setButtonState (state) {
    switch(state) {
    case ‘open’:
    connect.disabled = true
    sendMessage.disabled = false
    destroy.disabled = false
    break;
    case ‘close’:
    connect.disabled = false
    sendMessage.disabled = true
    destroy.disabled = true
    break;
    }
    }
    </script>
    </body>
    </html>

效果如下:

到了这一步,我们websocket的demo已经可以手动运行,在此基础上,我们将其封装一下,并且通过eventBus对外进行通信就可以用了,具体流程将与接下来的心跳一起实现
websocket心跳机制:顾名思义,就是客户端每隔一段时间向服务端发送一个特有的心跳消息,每次服务端收到消息后只需将消息返回,此时,若二者还保持连接,则客户端就会收到消息,若没收到,则说明连接断开,此时,客户端就要主动重连,完成一个周期
心跳的实现也很简单,只需在第一次连接时用回调函数做延时处理,此时还需要设置一个心跳超时时间,若某时间段内客户端发送了消息,而服务端未返回,则认定为断线。下面,我就来实现一下心跳

    //this.heartBeat  --->  time:心跳时间间隔 timeout:心跳超时间隔
    /*
     * 心跳初始函数
     * @param time:心跳时间间隔
     */
    function startHeartBeat (time) {
        setTimeout(() => {
            this.sendMsg({
                ModeCode: ModeCode.HEART_BEAT,
                msg: new Date()
            })
            this.waitingServer()
        }, time)
    }
    //延时等待服务端响应,通过webSocketState判断是否连线成功
    function waitingServer () {
        this.webSocketState = false//在线状态
        setTimeout(() => {
            if(this.webSocketState) {
                this.startHeartBeat(this.heartBeat.time)
                return
            }
            console.log('心跳无响应,已断线')
            this.close()
            //重连操作
        }, this.heartBeat.timeout)
    }

心跳实现完成后,只需要在ws.onopen中调用即可,效果如下:


然后是重连部分,其实只需要新建一个延时回调,与心跳相似,只不过它是在连接失败时使用的,这里就不多做说明。以下是完整版的代码:
websocket部分:

import eventBus
    from "./eventBus.js"

const ModeCode = {//websocket消息类型
MSG: ‘message’,//普通消息
HEART_BEAT: ‘heart_beat’//心跳
}

export default class MyWebSocket extends WebSocket {
constructor (url, protocols) {
super(url, protocols);
return this
}

<span class="hljs-comment">/*
 * 入口函数
 * @param heartBeatConfig  time:心跳时间间隔 timeout:心跳超时间隔 reconnect:断线重连时间间隔
 * @param isReconnect 是否断线重连
 */</span>
init (heartBeatConfig, isReconnect) {
    <span class="hljs-variable language_">this</span>.<span class="hljs-property">onopen</span> = <span class="hljs-variable language_">this</span>.<span class="hljs-property">openHandler</span><span class="hljs-comment">//连接上时回调</span>
    <span class="hljs-variable language_">this</span>.<span class="hljs-property">onclose</span> = <span class="hljs-variable language_">this</span>.<span class="hljs-property">closeHandler</span><span class="hljs-comment">//断开连接时回调</span>
    <span class="hljs-variable language_">this</span>.<span class="hljs-property">onmessage</span> = <span class="hljs-variable language_">this</span>.<span class="hljs-property">messageHandler</span><span class="hljs-comment">//收到服务端消息</span>
    <span class="hljs-variable language_">this</span>.<span class="hljs-property">onerror</span> = <span class="hljs-variable language_">this</span>.<span class="hljs-property">errorHandler</span><span class="hljs-comment">//连接出错</span>
    <span class="hljs-variable language_">this</span>.<span class="hljs-property">heartBeat</span> = heartBeatConfig
    <span class="hljs-variable language_">this</span>.<span class="hljs-property">isReconnect</span> = isReconnect
    <span class="hljs-variable language_">this</span>.<span class="hljs-property">reconnectTimer</span> = <span class="hljs-literal">null</span><span class="hljs-comment">//断线重连时间器</span>
    <span class="hljs-variable language_">this</span>.<span class="hljs-property">webSocketState</span> = <span class="hljs-literal">false</span><span class="hljs-comment">//socket状态 true为已连接</span>
}

openHandler () {
    eventBus.<span class="hljs-title function_">emitEvent</span>(<span class="hljs-string">'changeBtnState'</span>, <span class="hljs-string">'open'</span>)<span class="hljs-comment">//触发事件改变按钮样式</span>
    <span class="hljs-variable language_">this</span>.<span class="hljs-property">webSocketState</span> = <span class="hljs-literal">true</span><span class="hljs-comment">//socket状态设置为连接,做为后面的断线重连的拦截器</span>
    <span class="hljs-variable language_">this</span>.<span class="hljs-property">heartBeat</span> &amp;&amp; <span class="hljs-variable language_">this</span>.<span class="hljs-property">heartBeat</span>.<span class="hljs-property">time</span> ? <span class="hljs-variable language_">this</span>.<span class="hljs-title function_">startHeartBeat</span>(<span class="hljs-variable language_">this</span>.<span class="hljs-property">heartBeat</span>.<span class="hljs-property">time</span>) : <span class="hljs-string">""</span><span class="hljs-comment">//是否启动心跳机制</span>
    <span class="hljs-variable language_">console</span>.<span class="hljs-title function_">log</span>(<span class="hljs-string">'开启'</span>)
}

messageHandler (e) {
    <span class="hljs-keyword">let</span> data = <span class="hljs-variable language_">this</span>.<span class="hljs-title function_">getMsg</span>(e)
    <span class="hljs-keyword">switch</span>(data.<span class="hljs-property">ModeCode</span>) {
        <span class="hljs-keyword">case</span> <span class="hljs-title class_">ModeCode</span>.<span class="hljs-property">MSG</span>:<span class="hljs-comment">//普通消息</span>
            <span class="hljs-variable language_">console</span>.<span class="hljs-title function_">log</span>(<span class="hljs-string">'收到消息'</span> + data.<span class="hljs-property">msg</span>)
            <span class="hljs-keyword">break</span>;
        <span class="hljs-keyword">case</span> <span class="hljs-title class_">ModeCode</span>.<span class="hljs-property">HEART_BEAT</span>:<span class="hljs-comment">//心跳</span>
            <span class="hljs-variable language_">this</span>.<span class="hljs-property">webSocketState</span> = <span class="hljs-literal">true</span>
            <span class="hljs-variable language_">console</span>.<span class="hljs-title function_">log</span>(<span class="hljs-string">'收到心跳响应'</span> + data.<span class="hljs-property">msg</span>)
            <span class="hljs-keyword">break</span>;
    }
}

closeHandler () {<span class="hljs-comment">//socket关闭</span>
    eventBus.<span class="hljs-title function_">emitEvent</span>(<span class="hljs-string">'changeBtnState'</span>, <span class="hljs-string">'close'</span>)<span class="hljs-comment">//触发事件改变按钮样式</span>
    <span class="hljs-variable language_">this</span>.<span class="hljs-property">webSocketState</span> = <span class="hljs-literal">false</span><span class="hljs-comment">//socket状态设置为断线</span>
    <span class="hljs-variable language_">console</span>.<span class="hljs-title function_">log</span>(<span class="hljs-string">'关闭'</span>)
}

errorHandler () {<span class="hljs-comment">//socket出错</span>
    eventBus.<span class="hljs-title function_">emitEvent</span>(<span class="hljs-string">'changeBtnState'</span>, <span class="hljs-string">'close'</span>)<span class="hljs-comment">//触发事件改变按钮样式</span>
    <span class="hljs-variable language_">this</span>.<span class="hljs-property">webSocketState</span> = <span class="hljs-literal">false</span><span class="hljs-comment">//socket状态设置为断线</span>
    <span class="hljs-variable language_">this</span>.<span class="hljs-title function_">reconnectWebSocket</span>()<span class="hljs-comment">//重连</span>
    <span class="hljs-variable language_">console</span>.<span class="hljs-title function_">log</span>(<span class="hljs-string">'出错'</span>)
}

sendMsg (obj) {
    <span class="hljs-variable language_">this</span>.<span class="hljs-title function_">send</span>(<span class="hljs-title class_">JSON</span>.<span class="hljs-title function_">stringify</span>(obj))
}

getMsg (e) {
    <span class="hljs-keyword">return</span> <span class="hljs-title class_">JSON</span>.<span class="hljs-title function_">parse</span>(e.<span class="hljs-property">data</span>)
}

<span class="hljs-comment">/*
 * 心跳初始函数
 * @param time:心跳时间间隔
 */</span>
startHeartBeat (time) {
    <span class="hljs-built_in">setTimeout</span>(<span class="hljs-function">() =&gt;</span> {
        <span class="hljs-variable language_">this</span>.<span class="hljs-title function_">sendMsg</span>({
            <span class="hljs-title class_">ModeCode</span>: <span class="hljs-title class_">ModeCode</span>.<span class="hljs-property">HEART_BEAT</span>,
            <span class="hljs-attr">msg</span>: <span class="hljs-keyword">new</span> <span class="hljs-title class_">Date</span>()
        })
        <span class="hljs-variable language_">this</span>.<span class="hljs-title function_">waitingServer</span>()
    }, time)
}

<span class="hljs-comment">//延时等待服务端响应,通过webSocketState判断是否连线成功</span>
waitingServer () {
    <span class="hljs-variable language_">this</span>.<span class="hljs-property">webSocketState</span> = <span class="hljs-literal">false</span>
    <span class="hljs-built_in">setTimeout</span>(<span class="hljs-function">() =&gt;</span> {
        <span class="hljs-keyword">if</span>(<span class="hljs-variable language_">this</span>.<span class="hljs-property">webSocketState</span>) {
            <span class="hljs-variable language_">this</span>.<span class="hljs-title function_">startHeartBeat</span>(<span class="hljs-variable language_">this</span>.<span class="hljs-property">heartBeat</span>.<span class="hljs-property">time</span>)
            <span class="hljs-keyword">return</span>
        }
        <span class="hljs-variable language_">console</span>.<span class="hljs-title function_">log</span>(<span class="hljs-string">'心跳无响应,已断线'</span>)
        <span class="hljs-keyword">try</span> {
            <span class="hljs-variable language_">this</span>.<span class="hljs-title function_">close</span>()
        } <span class="hljs-keyword">catch</span>(e) {
            <span class="hljs-variable language_">console</span>.<span class="hljs-title function_">log</span>(<span class="hljs-string">'连接已关闭,无需关闭'</span>)
        }
        <span class="hljs-variable language_">this</span>.<span class="hljs-title function_">reconnectWebSocket</span>()
    }, <span class="hljs-variable language_">this</span>.<span class="hljs-property">heartBeat</span>.<span class="hljs-property">timeout</span>)
}

<span class="hljs-comment">//重连操作</span>
reconnectWebSocket () {
    <span class="hljs-keyword">if</span>(!<span class="hljs-variable language_">this</span>.<span class="hljs-property">isReconnect</span>) {
        <span class="hljs-keyword">return</span>;
    }
    <span class="hljs-variable language_">this</span>.<span class="hljs-property">reconnectTimer</span> = <span class="hljs-built_in">setTimeout</span>(<span class="hljs-function">() =&gt;</span> {
        eventBus.<span class="hljs-title function_">emitEvent</span>(<span class="hljs-string">'reconnect'</span>)
    }, <span class="hljs-variable language_">this</span>.<span class="hljs-property">heartBeat</span>.<span class="hljs-property">reconnect</span>)
}

}

index.html部分:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>
        Title</title>
</head>
<body>
<button id="connect">
    连接
</button>
<button disabled
        id="sendMessage">
    发送
</button>
<button disabled
        id="destroy">
    关闭
</button>
<script type="module">
    import eventBus
        from "./js/eventBus.js"
    import MyWebSocket
        from './js/webSocket.js'
<span class="hljs-keyword">const</span> name = <span class="hljs-string">'test'</span><span class="hljs-comment">//连接用户名</span>
<span class="hljs-keyword">let</span> connect = <span class="hljs-variable language_">document</span>.<span class="hljs-title function_">querySelector</span>(<span class="hljs-string">'#connect'</span>)
<span class="hljs-keyword">let</span> sendMessage = <span class="hljs-variable language_">document</span>.<span class="hljs-title function_">querySelector</span>(<span class="hljs-string">'#sendMessage'</span>)
<span class="hljs-keyword">let</span> destroy = <span class="hljs-variable language_">document</span>.<span class="hljs-title function_">querySelector</span>(<span class="hljs-string">'#destroy'</span>)
<span class="hljs-keyword">let</span> myWebSocket,
    wsUrl = <span class="hljs-string">'ws://127.0.0.1:1024/ws/?name='</span> + name

eventBus.<span class="hljs-title function_">onEvent</span>(<span class="hljs-string">'changeBtnState'</span>, setButtonState)<span class="hljs-comment">//设置按钮样式</span>
eventBus.<span class="hljs-title function_">onEvent</span>(<span class="hljs-string">'reconnect'</span>, reconnectWebSocket)<span class="hljs-comment">//接收重连消息</span>
connect.<span class="hljs-title function_">addEventListener</span>(<span class="hljs-string">'click'</span>, reconnectWebSocket)
sendMessage.<span class="hljs-title function_">addEventListener</span>(<span class="hljs-string">'click'</span>, <span class="hljs-keyword">function</span> (<span class="hljs-params">e</span>) {
    myWebSocket.<span class="hljs-title function_">sendMsg</span>({
        <span class="hljs-title class_">ModeCode</span>: <span class="hljs-string">"message"</span>,
        <span class="hljs-attr">msg</span>: <span class="hljs-string">'hello'</span>
    })
})
destroy.<span class="hljs-title function_">addEventListener</span>(<span class="hljs-string">'click'</span>, <span class="hljs-keyword">function</span> (<span class="hljs-params">e</span>) {
    myWebSocket.<span class="hljs-title function_">close</span>()
})

<span class="hljs-keyword">function</span> <span class="hljs-title function_">reconnectWebSocket</span> () {
    <span class="hljs-keyword">if</span>(!myWebSocket) {<span class="hljs-comment">//第一次执行,初始化</span>
        <span class="hljs-title function_">connectWebSocket</span>()
    }
    <span class="hljs-keyword">if</span>(myWebSocket &amp;&amp; myWebSocket.<span class="hljs-property">reconnectTimer</span>) {<span class="hljs-comment">//防止多个websocket同时执行</span>
        <span class="hljs-built_in">clearTimeout</span>(myWebSocket.<span class="hljs-property">reconnectTimer</span>)
        myWebSocket.<span class="hljs-property">reconnectTimer</span> = <span class="hljs-literal">null</span>
        <span class="hljs-title function_">connectWebSocket</span>()
    }
}

<span class="hljs-keyword">function</span> <span class="hljs-title function_">connectWebSocket</span> () {
    myWebSocket = <span class="hljs-keyword">new</span> <span class="hljs-title class_">MyWebSocket</span>(wsUrl);
    myWebSocket.<span class="hljs-title function_">init</span>({<span class="hljs-comment">//time:心跳时间间隔 timeout:心跳超时间隔 reconnect:断线重连时</span>
        <span class="hljs-attr">time</span>: <span class="hljs-number">30</span> * <span class="hljs-number">1000</span>,
        <span class="hljs-attr">timeout</span>: <span class="hljs-number">3</span> * <span class="hljs-number">1000</span>,
        <span class="hljs-attr">reconnect</span>: <span class="hljs-number">10</span> * <span class="hljs-number">1000</span>
    }, <span class="hljs-literal">true</span>)
}

<span class="hljs-comment">/*
 * 设置按钮是否可点击
 * @param state:open表示开启状态,close表示关闭状态
 */</span>
<span class="hljs-keyword">function</span> <span class="hljs-title function_">setButtonState</span> (state) {
    <span class="hljs-keyword">switch</span>(state) {
        <span class="hljs-keyword">case</span> <span class="hljs-string">'open'</span>:
            connect.<span class="hljs-property">disabled</span> = <span class="hljs-literal">true</span>
            sendMessage.<span class="hljs-property">disabled</span> = <span class="hljs-literal">false</span>
            destroy.<span class="hljs-property">disabled</span> = <span class="hljs-literal">false</span>
            <span class="hljs-keyword">break</span>;
        <span class="hljs-keyword">case</span> <span class="hljs-string">'close'</span>:
            connect.<span class="hljs-property">disabled</span> = <span class="hljs-literal">false</span>
            sendMessage.<span class="hljs-property">disabled</span> = <span class="hljs-literal">true</span>
            destroy.<span class="hljs-property">disabled</span> = <span class="hljs-literal">true</span>
            <span class="hljs-keyword">break</span>;
    }
}

</script>
</body>
</html>

最终实现的效果如下,即使后端服务关闭,或者是断网状态,客户端都能保持重连状态

最后,感谢你看到了这里,文章有任何问题欢迎大佬指出与讨论
附上源码:Gitee

 

原文:https://www.cnblogs.com/HelloWorld-Yu/p/14599514.html

Logo

旨在为数千万中国开发者提供一个无缝且高效的云端环境,以支持学习、使用和贡献开源项目。

更多推荐