最近在项目中需要用到
WebSocket。具体需求是, 有学生在跑圈。当他跑前,他身上的手表会给在起点的设备发消息。当他跑完后,他身上的手表会给在终点的设备发消息。最后在他跑完一圈后,后端接收到设备的数据,需要统计分析 给前端给一条数据。前端根据推的数据在大屏上显示出来。
一、简陋版
一开始写的比较简陋: 只是个 建立连接,发送数据的 过程。
贴下代码:
export default {
data () {
return {
socketExample: null,
sendArgs: {}
}
},
methods: {
// 1. 第一步 建立连接
initSocket: function (path = 'wss://zhcctest.sunfitlink.com/dcqWebSocket', args) {
if (typeof (WebSocket) === 'undefined') {
alert('您的浏览器不支持socketExample')
} else {
if (this.socketExample) {
this.sendArgs = args
this.send(JSON.stringify(this.sendArgs))
return
}
this.sendArgs = args
// 实例化socketExample
this.socketExample = new WebSocket(path)
// 监听socketExample连接
this.socketExample.onopen = this.socketExampleOpen
// 监听socketExample错误信息
this.socketExample.onerror = this.socketExampleError
// 监听socketExample消息
this.socketExample.onmessage = this.socketExampleGetMessage
this.socketExample.onclose = this.socketExampleClose
}
},
// 2. 第二步 连接成功后开始发送数据
socketExampleOpen: function () {
this.send(JSON.stringify(this.sendArgs))
},
socketExampleError: function () {
console.group('连接错误')
},
socketExampleGetMessage: function (event) {},
send: function (params) {
this.socketExample.send(params)
},
socketExampleClose: function () {
console.group('socketExample已经关闭')
}
},
destroyed () {
// 销毁监听
this.socketExample && this.socketExample.close()
this.socketExample = null
}
}
采用的是 vue 的 mixins 的方式。
二、 完整版
在测试的时候遇到了一个比较严重的问题。就是当连接上的时候,不知道是不是网络原因,总是会断开连接。
后来新增了心跳机制(每隔10秒前端给后端发送一条心跳数据,后端返回一条心跳数据)。
但是还是会有时候会断开连接。
于是,又新增了 心跳检测、断开重新连接 功能。
至此,项目运行成功!
下面看完整代码:
export const WSMixins = {
data () {
return {
// websocket 实例, 理论上是唯一的
WS_socketExample: null,
// 参数
WS_path: '',
WS_args: '',
WS_isOpenHeartbeat: false,
WS_getMsgCallBack: null,
// 心跳机制的 timer
WS_heartbeatTimer: null,
// 心跳监测的 timer
WS_heartCheckTimer: null,
// 重连 锁
WS_lockReconnect: false,
// 监测心跳是否响应的 timer
WS_checkIsResTimer: null,
WS_status: ''
}
},
methods: {
/**
* 初始化
* @param {String} path 路径
* @param {Object} args 参数
* @param {Boolean} isOpenHeartbeat 是否开启心跳机制
* @param {Function} getMsgCallBack 消息接受到后 触发的外部函数
* @returns
*/
WS_initSocket ({
path,
args,
isOpenHeartbeat = false,
getMsgCallBack
}) {
if (typeof WebSocket === 'undefined') {
alert('您的浏览器不支持WebSocket')
} else {
if (this.WS_socketExample) {
this.WS_reset()
}
// 记录参数
this.WS_path = path
this.WS_args = args
this.WS_isOpenHeartbeat = isOpenHeartbeat
this.WS_getMsgCallBack = getMsgCallBack
this.WS_socketExample = new WebSocket(path)
// 实例化后,进行监听
this.WS_socketExample.onopen = this.WS_socketExampleOpen
this.WS_socketExample.onerror = this.WS_socketExampleError
this.WS_socketExample.onmessage = this.WS_socketExampleGetMessage
this.WS_socketExample.onclose = this.WS_socketExampleClose
}
},
WS_reset () {
this.WS_socketExample = null
this.WS_path = ''
this.WS_args = ''
this.WS_isOpenHeartbeat = false
clearTimeout(this.WS_heartbeatTimer)
this.WS_heartbeatTimer = null
clearInterval(this.WS_heartCheckTimer)
this.WS_heartCheckTimer = null
clearTimeout(this.WS_checkIsResTimer)
this.WS_checkIsResTimer = null
clearTimeout(this.WS_reconnectTimer)
this.WS_reconnectTimer = null
},
// 连接建立时触发
WS_socketExampleOpen () {
// 开启心跳机制
if (this.WS_isOpenHeartbeat) {
this.WS_startHeartbeat()
}
if (this.WS_args) {
this.WS_socketExample.send(JSON.stringify(this.WS_args))
}
},
// 通信发生错误时触发
WS_socketExampleError (error) {
console.log('遇到错误了:', error)
this.WS_reconnect()
},
// 客户端接受服务端数据时触发
WS_socketExampleGetMessage (res) {
// 如果接受到信息, 则重新开始下一轮心跳机制
if (this.WS_isOpenHeartbeat) {
this.WS_startHeartbeat()
}
this.WS_getMsgCallBack && this.WS_getMsgCallBack(res)
},
// 连接关闭时触发
WS_socketExampleClose () {
// 如果不是主动关闭
if (this.WS_status !== 'close') {
this.WS_reconnect()
}
},
// 开始心跳机制
WS_startHeartbeat () {
clearTimeout(this.WS_heartbeatTimer)
this.WS_heartbeatTimer = null
clearTimeout(this.WS_checkIsResTimer)
this.WS_checkIsResTimer = null
this.WS_heartbeatTimer = setTimeout(() => {
if (this.WS_socketExample.readyState === 1) {
this.WS_socketExample.send(JSON.stringify({
// 后端要求心跳的时候也需要传递参数过去
...this.WS_args,
startHeartbeat: true
}))
// 心跳发送后,如果服务器未响应则断开,如果响应了会被重置下面的定时器
this.WS_checkIsResTimer = setTimeout(() => {
this.WS_socketExample.close()
}, 1000 * 10)
} else {
this.WS_reconnect()
}
}, 1000 * 10)
},
// 重新连接
WS_reconnect () {
if (this.WS_socketExample.readyState === 1) {
// 如果状态等于1导表 ws 连接正常
return
}
if (this.WS_lockReconnect) {
return
}
this.WS_lockReconnect = true
this.WS_reconnectTimer = setTimeout(() => {
console.log('尝试重连...')
this.WS_initSocket({
path: this.WS_path,
args: this.WS_args,
isOpenHeartbeat: this.WS_isOpenHeartbeat,
getMsgCallBack: this.WS_getMsgCallBack
})
this.WS_lockReconnect = false
clearTimeout(this.WS_reconnectTimer)
this.WS_reconnectTimer = null
}, 5000)
},
// 心跳监测
WS_heartCheck () {
this.WS_heartCheckTimer = setInterval(() => {
this.WS_reconnect()
}, 1500)
}
},
destroyed () {
this.WS_status = 'close'
this.WS_reset()
}
}
依旧是采用 mixins 的方式,下面是在页面中调用的代码:
import { WSMixins } from '@/utils/socketMixins.js'
export default {
mixins: [WSMixins],
created () {
startSocket()
},
startSocket () {
this.WS_initSocket({
path: `wss://socket路径`,
args: {参数 },
isOpenHeartbeat: true, // 是否开启心跳
getMsgCallBack: ({ data }) => {
// 接收到消息的回调
// 对于我的业务来说,这里就是显示大屏页面
})
},