问题描述:
在前端经常会存在一种情况就是实时接受后端通知数据,例如消息通知。如果在前端做一个定时器一直刷新接口虽然也能获取数据,但是显然不太合适。因为不清楚什么实时会更新数据,会造成大量的资源浪费。
socket通讯简单化处理前端和服务端的通讯问题。在websocket中,浏览器和服务器只需要完成一次握手,两者之间就直接可以创建持久性的连接,并进行双向数据传输
第一步:创建webSocket.ts 文件
var wsurl = ''
let ws: WebSocket | null = null
let weboscket_callback: any
let timeoutObj: any = null
let serverTimeoutObj: any = null
let lockReconnect = false// 避免ws重复连接
let connectNum = 0
// 获取 websocket 推送的数据
const websocketonmessage = (e: any) => {
heartCheck.reset()
heartCheck.start()
connectNum = 0
if (e.data === 'pong') {
// console.log(e)
} else {
return weboscket_callback(e)
}
}
// 连接成功
const websocketonopen = () => {
heartCheck.reset()
heartCheck.start()
connectNum = 0
console.log('连接 websocket 成功')
}
// 连接失败时重新连接
const websocketonerror = () => {
reconnect(wsurl)
}
// 断开链接后报错
const websocketclose = (e: any) => {
console.log('断开连接', e)
reconnect(wsurl)
}
// 手动关闭 websocket
const closewebsocket = () => {
ws.close()
}
// 初始化socket
const initWebSocket = (url: string) => {
wsurl = url
// 初始化 websocket
ws = new WebSocket(wsurl)
ws.onmessage = websocketonmessage
ws.onopen = websocketonopen
ws.onerror = websocketonerror
ws.onclose = websocketclose
}
// 发送数据
const sendData = (info: any) => {
// 判断 data 数据类型
let data = ''
if (typeof info === 'string') {
data = info
} else {
data = JSON.stringify(info)
}
// 判断 websocket 的状态
if (ws.readyState === ws.OPEN) {
// 已经打开,可以直接发送
ws.send(data)
} elseif (ws.readyState === ws.CONNECTING) {
// 正在开启状态中,则 1 秒后重新发送
setTimeout(() => {
ws.send(data)
}, 1000)
} else {
// 未打开,则开启后重新调用
reconnect(wsurl)
sendData(data)
}
}
// 接受数据
const acceptData = (callback: any) => {
weboscket_callback = callback
}
// 重连机制
function reconnect(url: string) {
if (lockReconnect || connectNum > 60) return
lockReconnect = true
setTimeout(function () {
// 没连接上会一直重连,设置延迟避免请求过多
initWebSocket(url)
lockReconnect = false
connectNum++
}, 5000)
}
// 心跳检测
const heartCheck = {
reset:function () {
clearTimeout(timeoutObj)
clearTimeout(serverTimeoutObj)
},
start:function () {
timeoutObj = setTimeout(function () {
// 这里发送一个心跳,后端收到后,返回一个心跳消息,
// onmessage拿到返回的心跳就说明连接正常
ws.send('ping')
// console.log('ping!')
serverTimeoutObj = setTimeout(function () {
console.log('超时关闭')
// 如果超过1分钟还没重置,说明后端主动断开了
ws.close() // 如果onclose会执行reconnect,我们执行ws.close()就行了.如果直接执行reconnect 会触发 onclose导致重连两次
}, 600000)
}, 60000)
}
}
// 导出
export { initWebSocket, sendData, acceptData, closewebsocket }
第二步:在登录的时候创建sockted通信,只需要创建一次即可,后续可以直接通过 acceptData 进行数据接受
import { initWebSocket } from'@/utils/webSocket'
if (userInfo) {
initWebSocket(`wss://xxxx.com/live/websocket/${userInfo.uid}`)
}
第三步:在main.ts中重新创建sockted链接,同步骤2
注意事项:
1.在退出登录时需要关闭sockted通信,避免socket通道一直连接造成通信资源浪费
import { closewebsocket } from'@/utils/webSocket'
closewebsocket()
2.sockted通信可能会异常中断(例如服务异常,超时等),可以通过建立心跳检测机制,检测通信状态,异常情况重新建立通信。但是会存在服务关闭,需要做一个最大重连时长。参考心跳检测逻辑
3.在页面刷新或则热更新的时候会中断socket通讯
处理方式:在main.ts中重新进行链接
import { initWebSocket } from'@/utils/webSocket'
if (userInfo) {
initWebSocket(`wss://xxxx.com/live/websocket/${userInfo.uid}`)
}
参考文档:
https://blog.csdn.net/jingyoushui/article/details/111318414
https://www.cnblogs.com/lyt520/p/14921989.html
https://www.cnblogs.com/gxp69/p/11736749.html
https://blog.csdn.net/weixin_39727976/article/details/111164100