基本实现
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
client
重连
现在存在一个问题:当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字段判断是否需要重连。
效果如下:
存在问题
问题
上面代码看起来没问题(题主本来也看起来没问题,后续工作中发现存在问题,功能代码中依赖onmessage的方法在server返回一次的情况下重复调用)
在连接上的时候isClosed字段才设置为false(不是关闭状态),在连接不能及时返回(如网络问题)导致在下次定时器工作时还未连接,重复创建一遍WebSocket
有同学可能要说了(包括题主:D),ws是全局的,下次创建会覆盖,会被回收,但是真的是这样的吗?:
在初始阻塞(题主模拟了弱网状态)的情况下,创建了多个socket并且没有被销毁,持续调用onmessage
解决方法
- 方案一:把isClosed放到init方法中而不是回调中,这样就不会受网络阻塞等影响重复创建
- 方案二:不使用自己创建的变量,使用WebSocket.readyState状态判断是否需要连接,详见[MDN]