问题描述:
    在前端经常会存在一种情况就是实时接受后端通知数据,例如消息通知。如果在前端做一个定时器一直刷新接口虽然也能获取数据,但是显然不太合适。因为不清楚什么实时会更新数据,会造成大量的资源浪费。
    socket通讯简单化处理前端和服务端的通讯问题。在websocket中,浏览器和服务器只需要完成一次握手,两者之间就直接可以创建持久性的连接,并进行双向数据传输

    第一步:创建webSocket.ts 文件

    1. var wsurl = ''
    2. let ws: WebSocket | null = null
    3. let weboscket_callback: any
    4. let timeoutObj: any = null
    5. let serverTimeoutObj: any = null
    6. let lockReconnect = false// 避免ws重复连接
    7. let connectNum = 0
    8. // 获取 websocket 推送的数据
    9. const websocketonmessage = (e: any) => {
    10. heartCheck.reset()
    11. heartCheck.start()
    12. connectNum = 0
    13. if (e.data === 'pong') {
    14. // console.log(e)
    15. } else {
    16. return weboscket_callback(e)
    17. }
    18. }
    19. // 连接成功
    20. const websocketonopen = () => {
    21. heartCheck.reset()
    22. heartCheck.start()
    23. connectNum = 0
    24. console.log('连接 websocket 成功')
    25. }
    26. // 连接失败时重新连接
    27. const websocketonerror = () => {
    28. reconnect(wsurl)
    29. }
    30. // 断开链接后报错
    31. const websocketclose = (e: any) => {
    32. console.log('断开连接', e)
    33. reconnect(wsurl)
    34. }
    35. // 手动关闭 websocket
    36. const closewebsocket = () => {
    37. ws.close()
    38. }
    39. // 初始化socket
    40. const initWebSocket = (url: string) => {
    41. wsurl = url
    42. // 初始化 websocket
    43. ws = new WebSocket(wsurl)
    44. ws.onmessage = websocketonmessage
    45. ws.onopen = websocketonopen
    46. ws.onerror = websocketonerror
    47. ws.onclose = websocketclose
    48. }
    49. // 发送数据
    50. const sendData = (info: any) => {
    51. // 判断 data 数据类型
    52. let data = ''
    53. if (typeof info === 'string') {
    54. data = info
    55. } else {
    56. data = JSON.stringify(info)
    57. }
    58. // 判断 websocket 的状态
    59. if (ws.readyState === ws.OPEN) {
    60. // 已经打开,可以直接发送
    61. ws.send(data)
    62. } elseif (ws.readyState === ws.CONNECTING) {
    63. // 正在开启状态中,则 1 秒后重新发送
    64. setTimeout(() => {
    65. ws.send(data)
    66. }, 1000)
    67. } else {
    68. // 未打开,则开启后重新调用
    69. reconnect(wsurl)
    70. sendData(data)
    71. }
    72. }
    73. // 接受数据
    74. const acceptData = (callback: any) => {
    75. weboscket_callback = callback
    76. }
    77. // 重连机制
    78. function reconnect(url: string) {
    79. if (lockReconnect || connectNum > 60) return
    80. lockReconnect = true
    81. setTimeout(function () {
    82. // 没连接上会一直重连,设置延迟避免请求过多
    83. initWebSocket(url)
    84. lockReconnect = false
    85. connectNum++
    86. }, 5000)
    87. }
    88. // 心跳检测
    89. const heartCheck = {
    90. reset:function () {
    91. clearTimeout(timeoutObj)
    92. clearTimeout(serverTimeoutObj)
    93. },
    94. start:function () {
    95. timeoutObj = setTimeout(function () {
    96. // 这里发送一个心跳,后端收到后,返回一个心跳消息,
    97. // onmessage拿到返回的心跳就说明连接正常
    98. ws.send('ping')
    99. // console.log('ping!')
    100. serverTimeoutObj = setTimeout(function () {
    101. console.log('超时关闭')
    102. // 如果超过1分钟还没重置,说明后端主动断开了
    103. ws.close() // 如果onclose会执行reconnect,我们执行ws.close()就行了.如果直接执行reconnect 会触发 onclose导致重连两次
    104. }, 600000)
    105. }, 60000)
    106. }
    107. }
    108. // 导出
    109. export { initWebSocket, sendData, acceptData, closewebsocket }

    第二步:在登录的时候创建sockted通信,只需要创建一次即可,后续可以直接通过 acceptData 进行数据接受

    1. import { initWebSocket } from'@/utils/webSocket'
    2. if (userInfo) {
    3. initWebSocket(`wss://xxxx.com/live/websocket/${userInfo.uid}`)
    4. }

    第三步:在main.ts中重新创建sockted链接,同步骤2

    注意事项:
    1.在退出登录时需要关闭sockted通信,避免socket通道一直连接造成通信资源浪费

    1. import { closewebsocket } from'@/utils/webSocket'
    2. closewebsocket()

    2.sockted通信可能会异常中断(例如服务异常,超时等),可以通过建立心跳检测机制,检测通信状态,异常情况重新建立通信。但是会存在服务关闭,需要做一个最大重连时长。参考心跳检测逻辑

    3.在页面刷新或则热更新的时候会中断socket通讯
    处理方式:在main.ts中重新进行链接

    1. import { initWebSocket } from'@/utils/webSocket'
    2. if (userInfo) {
    3. initWebSocket(`wss://xxxx.com/live/websocket/${userInfo.uid}`)
    4. }

    参考文档:
    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