问题描述:
在前端经常会存在一种情况就是实时接受后端通知数据,例如消息通知。如果在前端做一个定时器一直刷新接口虽然也能获取数据,但是显然不太合适。因为不清楚什么实时会更新数据,会造成大量的资源浪费。
socket通讯简单化处理前端和服务端的通讯问题。在websocket中,浏览器和服务器只需要完成一次握手,两者之间就直接可以创建持久性的连接,并进行双向数据传输
第一步:创建webSocket.ts 文件
var wsurl = ''let ws: WebSocket | null = nulllet weboscket_callback: anylet timeoutObj: any = nulllet serverTimeoutObj: any = nulllet lockReconnect = false// 避免ws重复连接let connectNum = 0// 获取 websocket 推送的数据const websocketonmessage = (e: any) => {heartCheck.reset()heartCheck.start()connectNum = 0if (e.data === 'pong') {// console.log(e)} else {return weboscket_callback(e)}}// 连接成功const websocketonopen = () => {heartCheck.reset()heartCheck.start()connectNum = 0console.log('连接 websocket 成功')}// 连接失败时重新连接const websocketonerror = () => {reconnect(wsurl)}// 断开链接后报错const websocketclose = (e: any) => {console.log('断开连接', e)reconnect(wsurl)}// 手动关闭 websocketconst closewebsocket = () => {ws.close()}// 初始化socketconst initWebSocket = (url: string) => {wsurl = url// 初始化 websocketws = new WebSocket(wsurl)ws.onmessage = websocketonmessagews.onopen = websocketonopenws.onerror = websocketonerrorws.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) returnlockReconnect = truesetTimeout(function () {// 没连接上会一直重连,设置延迟避免请求过多initWebSocket(url)lockReconnect = falseconnectNum++}, 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
