websocket前端封装代码,心跳机制断线重连

websocket简介(可跳过)

websocket是一种全双工通信长链接,大多用来实现及时通讯,数据实时性要求较为高的地方,在websoket未出现的时候前端使用的setInterval轮训进行数据更新的,在那些对于数据实时性要求不高地方我们仍可以使用 轮训。

(1)建立在 TCP 协议之上,服务器端的实现比较容易。

(2)与 HTTP 协议有着良好的兼容性。默认端口也是80和443,并且握手阶段采用 HTTP 协议,因此握手时不容易屏蔽,能通过各种 HTTP 代理服务器。

(3)数据格式比较轻量,性能开销小,通信高效。

(4)可以发送文本,也可以发送二进制数据。

(5)没有同源限制,客户端可以与任意服务器通信。

(6)通常的协议标示符号为http,而websoket是ws(默认端口80),加密通信为wss(默认端口443)对应为https

websocket前端封装代码,心跳机制断线重连_第1张图片

Http-长短链接

HTTP连接分为长连接和短连接,而我们现在常用的都是HTTP1.1,因此我们用的都是长连接。这句话其实只对了一半,我们现如今的HTTP协议,大部分都是1.1的,因此我们平时用的基本上都是长连接。HTTP协议根本没有长短连接这一说。HTTP协议是基于请求/响应模式的,因此只要服务端给了响应,本次HTTP连接就结束了,或者更准确的说,是本次HTTP请求就结束了,根本没有长短连接这一说,

TCP-长短链接

长连接和短连接,其实是说的TCP连接。TCP连接是一个双向的通道,它是可以保持一段时间不关闭的,因此TCP连接才有真正的长连接和短连接。HTTP协议说到底是应用层的协议,而TCP才是真正的传输层协议,只有负责传输的这一层才需要建立连接。

就像快递 填的快递地址单号是http,而运输工具例如车 对应的是tcp

websocket 心跳机制/断线重连机制

当双方通讯都已经链接,如果没有外在原因,双方不断开链接本质上链接不会中断。

但是存在的外在因素会让websoket中断例如:(网络断线,网络状态不佳,链接需要在内网而内网无法联机,链接的进程被杀死/服务器长时间无通讯)

为了保证这个通信一直保持鲜活 我们需要增加 心跳机制/断线重连机制

websocket前端封装代码,心跳机制断线重连_第2张图片
心跳机制

在与服务器建立起链接的时候(open) 开始心跳

每隔30s(或者指定时间 这里都暂用30s)向服务器 send 一条心跳消息 ⌛等待 服务器给 答复

如果在指定30s 前端收到服务器返回的消息(message), 重置心跳 重新开始计时30s 再次心跳。

如果没有收到消息(可能是网络等原因) 服务器无应答 调用websocke.close() 关闭这个长链接 再重新发送新链接请求 请求重新连接 服务器

重连机制

我们分别在 websokcet 报错/close/errno/无响应(这里心跳直接调用close,再用close进行重新连接)

封装 具体代码

调用方法

import { socketContact } from '@/utils/websocket'
const wss = socketContact('/ws/quality_panel/', 'quality panel data', (data) => {
  tableData.value = data // 可以自定义数据处理
})

具体封装代码:

const socketContact = function (contactUrl = '', getDataSecret = '', getDataFn = () => {}) {
  const baseUrl = process.env.VUE_APP_API_WEBSOCKET_URL // baseURL
  const callBack = getDataFn // callback返回数据函数
  const url = contactUrl // 后缀url
  // getDataSecret 需要发送给服务器的数据
  let socket // websocket本体
  let lock = false // 避免重复连接
  let replyOverTime = null // 服务器回复超时setTimeout
  let hearByteTime = null // 心跳 setTimeout
  try {
    socket = new WebSocket(baseUrl + url)
    socket.addEventListener('open', function (event) {
      socket.send(getDataSecret)
      heatByte() // 开始心跳
      console.log('链接成功', contactUrl, getDataSecret)
    })
    socket.addEventListener('message', function (event) {
      resetHardByte() // 重置心跳
      // 这里transporting...' 和'invalid command...'为服务器心跳回复 不做数据返回处理
      if (event.data === 'transporting...' || event.data === 'invalid command...') return
      // 处理服务器返回数据 这里可自定义
      const str = event.data.replaceAll('None', 'null')
      // eslint-disable-next-line no-eval
      const data = eval('(' + str + ')')
      callBack(data)
    });
    socket.addEventListener('close', function (event) {
      console.log('链接关闭', event.code)
      reconnect(url, getDataSecret, callBack) // 尝试重新连接
    });
    socket.addEventListener('errno', function (event) {
      console.log('链接报错')
      reconnect(url, getDataSecret, callBack)// 尝试重新连接
    });
  } catch (e) {
    console.log('catch', e)
    reconnect(url, getDataSecret, callBack) // 尝试重新连接
  }
  function heatByte (type) {
    hearByteTime && clearInterval(hearByteTime)
    replyOverTime && clearInterval(replyOverTime)
    // 每30s一次心跳
    hearByteTime = setTimeout(() => {
      if (socket.readyState === 1) {
        socket.send('heart byte') // 没问题进行心跳
        console.log('heart byte')
      }
      // 重新链接超时 60s无回复 关闭连接 尝试重新链接
      replyOverTime = setTimeout(() => {
        socket.close()
        console.log('网络/其他问题 回复超时间关闭')
      }, 60000)
    }, 30000)
  }
  function resetHardByte () {
    // 进行下一次心跳
    hearByteTime && clearTimeout(hearByteTime)
    replyOverTime && clearTimeout(replyOverTime)
    heatByte()
  }
  function reconnect (url, getDataSecret, callBack) {
    if (lock) return
    lock = true
    setTimeout(() => {
      lock = false
      socketContact(url, getDataSecret, callBack)
      console.log('正在尝试重新连接')
    }, 5000)
  }
  return socket
}

export { socketContact }
参考

长链接短连接:http://t.csdn.cn/Ne7g3

websokcet 阮一峰:https://www.ruanyifeng.com/blog/2017/05/websocket.html

websokcet:http://t.csdn.cn/j42h5

你可能感兴趣的:(前端,vue.js,websocket)