简易的 Websocket + 心跳机制 + 尝试重连


还没有写完,bug 是有的,我在想解决办法了…

演示

请添加图片描述


大纲

  • 基础的 webSocket 连接
  • 前后端:添加心跳机制
  • 后端无心跳反应,前端尝试重新连接
  • 设置重新连接次数,超过最大尝试次数之后,不再尝试重新连接

基础 WebSocket

前端的基础就是这些,大概的效果是这样的

请添加图片描述

<body>
  <button onclick="reConnect()">1. 重建连接</button>
  <button onclick="sendMessage()">2. 发消息</button>
  <button onclick="stopConnect()">3. 断开连接</button>
</body>
<script>
  let ws = null // 使用null来标记当前没有活动的 WebSocket 连接

  function createNewWebSocket() {
    if (ws && ws.readyState !== WebSocket.CLOSED) {
      ws.close() // 确保关闭旧的连接
    }
    ws = new WebSocket('ws://localhost:8080')

    ws.onopen = function (evt) {
      console.log('Connection open ...')
    }

    ws.onmessage = function (evt) {
      console.log('Received Message: ' + evt.data)
    }

    ws.onclose = function (evt) {
      console.log('Connection closed.')
    }
  }

  function sendMessage() {
    if (ws) ws.send(`前端发送:>> ${new Date()}`)
  }

  function stopConnect() {
    if (ws) ws.close()
  }

  function reConnect() {
    createNewWebSocket()
  }
</script>

后端的代码基本不变,所以我直接把心跳也做好
后端的心跳就是:拿到前端的值,如果是 ping 的话,就返回一个 pong,其他逻辑保持不变

const http = require('http')
const WebSocket = require('ws')
const server = http.createServer()
const wss = new WebSocket.Server({ server })

wss.on('connection', (socket) => {
  console.log('webSocket 连接成功')

  socket.on('message', (message) => {
    // 将 Buffer 转换为字符串
    const messageStr = message.toString()
    const currentRandom = Math.random()
    const isSendPong = currentRandom < 0.5
    console.log('后端收到消息:>>' + messageStr)
    // 检查是否为心跳请求
    if (messageStr === 'ping') {
      socket.send(`当前随机值为 ${currentRandom}, 是否发送心跳:${isSendPong}`)
      //  50%的概率发送 "pong"
      if (isSendPong) {
        socket.send('pong') // 心跳响应
      }
    } else {
      const message = `后端发送消息:>> 你好前端~ ${new Date().toLocaleString()}`
      socket.send(message)
    }
  })

  socket.on('close', () => {
    console.log('websocket 已经关闭')
  })
})

server.on('request', (request, response) => {
  response.writeHead(200, { 'Content-Type': 'text/plain' })
  response.end('Hello, World')
})

server.listen(8080, () => {
  console.log('服务器已启动,端口号为 8080')
})


前端: 添加心跳机制

思路:前端写一个定时器,用于隔一段时间发送一个 ping
效果如下图所示请添加图片描述

好吧,false 的概率有点高,不顾可以看历史记录

我在后端设置了随机逻辑,模拟一下出错的场景,百分之50的概率回应前端的心跳,如果 true 的话,后端就回应前端的心跳,返回 pong

前端 代码如下

<body>
  <button onclick="reConnect()">1. 重建连接</button>
  <button onclick="sendMessage()">2. 发消息</button>
  <button onclick="stopConnect()">3. 断开连接</button>
</body>
<script>
  let ws = null // 使用null来标记当前没有活动的 WebSocket 连接
  let heartbeatTimer = null // 心跳定时器
  const HEARTBEAT_INTERVAL = 5000 // 每隔 5 秒发送一次心跳

  function createNewWebSocket() {
    if (ws && ws.readyState !== WebSocket.CLOSED) {
      ws.close() // 确保关闭旧的连接
    }
    ws = new WebSocket('ws://localhost:8080')

    ws.onopen = function (evt) {
      console.log('Connection open ...')
      startHeartbeat()
    }

    ws.onmessage = function (evt) {
      console.log('Received Message: ' + evt.data)
      handleHeartbeatResponse(evt.data)
    }

    ws.onclose = function (evt) {
      console.log('Connection closed.')
      stopHeartbeat()
    }
  }

  function sendMessage() {
    if (ws) ws.send(`前端发送:>> ${new Date().toLocaleString()}`)
  }

  function stopConnect() {
    if (ws) ws.close()
    stopHeartbeat()
  }

  function reConnect() {
    createNewWebSocket()
  }

  function startHeartbeat() {
    heartbeatTimer = setInterval(() => {
      ws.send('ping')
    }, HEARTBEAT_INTERVAL)
  }

  function stopHeartbeat() {
    clearInterval(heartbeatTimer)
    heartbeatTimer = null
  }

  function handleHeartbeatResponse(message) {
    if (message === 'heartbeat') {
      console.log('Heartbeat received.')
      clearTimeout(heartbeatTimer) // 清除超时定时器
      startHeartbeat() // 重新启动心跳
    }
  }
</script>

前端: 尝试重新连接

设置一个场景: 前端发送三个心跳包,如果都没有反应,那么就判断为断开连接了,就去重新连接

历史代码

前端

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Document</title>
  </head>
  <body>
    <button id="btn">发消息</button>
    <button id="stop">断链</button>
    <button id="reconnect">重新连接</button>
  </body>
  <script>
    const btn = document.querySelector('#btn')
    const stop = document.querySelector('#stop')
    const reconnect = document.querySelector('#reconnect')
    let ws = null // 使用null来标记当前没有活动的WebSocket连接
    let heartbeatInterval = null // 存储心跳定时器
    let timeoutId = null // 存储超时定时器
    let reconnectAttempts = 0 // 重连尝试次数
    const maxReconnectAttempts = 3 // 最大重连次数

    function createNewWebSocket() {
      if (ws && ws.readyState !== WebSocket.CLOSED) {
        ws.close() // 确保关闭旧的连接
      }
      ws = new WebSocket('ws://localhost:8080')

      ws.onopen = function (evt) {
        console.log('Connection open ...')
        startHeartbeat() // 开始发送心跳
      }

      ws.onmessage = function (evt) {
        console.log('Received Message: ' + evt.data)

        // 检查是否为心跳响应
        if (evt.data === 'pong') {
          clearTimeout(timeoutId) // 清除超时定时器
          resetHeartbeatTimer() // 重置心跳定时器
        }
      }

      ws.onclose = function (evt) {
        console.log('Connection closed.')
        clearInterval(heartbeatInterval) // 清除心跳定时器
        reconnectWebSocket() // 尝试重新连接
      }
    }

    // 发送心跳包
    function sendHeartbeat() {
      if (ws) {
        ws.send('ping')
        timeoutId = setTimeout(() => {
          // 如果在超时时间内没有收到 "pong" 响应,则关闭当前连接
          console.log('超时,关闭连接')
          ws.close()
        }, 15000) // 设置超时时间为 15 秒
      }
    }

    // 启动心跳
    function startHeartbeat() {
      sendHeartbeat() // 立即发送第一个心跳包
      heartbeatInterval = setInterval(sendHeartbeat, 30000) // 每30秒发送一次
    }

    // 重置心跳定时器
    function resetHeartbeatTimer() {
      clearInterval(heartbeatInterval)
      heartbeatInterval = setInterval(sendHeartbeat, 30000) // 重新设置定时器
    }

    // 重新连接
    function reconnectWebSocket() {
      console.log('尝试重新连接', reconnectAttempts)

      // 检查是否超过最大重连次数
      if (reconnectAttempts < maxReconnectAttempts) {
        reconnectAttempts++
        createNewWebSocket()
      } else {
        console.log('超过最大重连次数,不再尝试连接')
      }
    }

    btn.addEventListener('click', () => {
      if (ws) {
        ws.send(`前端发送:>> ${new Date()}`)
      }
    })

    stop.addEventListener('click', () => {
      if (ws) {
        ws.close()
      }
    })

    reconnect.addEventListener('click', () => {
      createNewWebSocket()
    })
  </script>
</html>

后端

const http = require('http')
const WebSocket = require('ws')
const server = http.createServer()
const wss = new WebSocket.Server({ server })

wss.on('connection', (socket) => {
  console.log('webSocket 连接成功')

  socket.on('message', (message) => {
    // 将 Buffer 转换为字符串
    const messageStr = message.toString();

    console.log('后端收到消息:>>' + messageStr);

    // 检查是否为心跳请求
    if (messageStr === 'ping') {
      socket.send('pong'); // 心跳响应
    } else {
      socket.send('后端发送消息:>> hello 我是 socket.send');
    }
  })

  socket.on('close', () => {
    console.log('websocket 已经关闭');
  })
})

server.on('request', (request, response) => {
  response.writeHead(200, { 'Content-Type': 'text/plain' });
  response.end('Hello, World');
})

server.listen(8080, () => {
  console.log('服务器已启动,端口号为 8080');
})

  • 7
    点赞
  • 22
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
好的,我理解你的问题是关于使用 `setup` 语法糖封装 WebSocket心跳机制的方法。首先, `setup` 是 Vue 3 中引入的新特性,它可以在组件实例化之前执行一些操作,比如初始化状态、订阅事件、创建 WebSocket 连接等。下面是一个使用 `setup` 语法糖封装 WebSocket心跳机制的示例代码: ```vue <template> <div>{{ message }}</div> </template> <script> import { ref, onMounted, onUnmounted } from 'vue' export default { setup() { const message = ref('') let socket = null const connectWebSocket = () => { socket = new WebSocket('wss://example.com/ws') socket.onopen = () => { console.log('WebSocket connected') socket.send('Hello, WebSocket') } socket.onmessage = event => { message.value = event.data } socket.onclose = () => { console.log('WebSocket disconnected') setTimeout(() => { connectWebSocket() }, 5000) } } const startHeartbeat = () => { setInterval(() => { if (socket.readyState === WebSocket.OPEN) { socket.send('Heartbeat') } }, 3000) } onMounted(() => { connectWebSocket() startHeartbeat() }) onUnmounted(() => { socket.close() }) return { message } } } </script> ``` 上面的代码中,我们使用了 `ref` 创建了一个响应式的 `message` 变量,将其绑定到模板中的 `<div>` 元素上。在 `setup` 函数中,我们声明了一个 `socket` 变量,用于存储 WebSocket 连接对象。然后,我们定义了 `connectWebSocket` 函数,用于创建 WebSocket 连接,并在连接打开、收到消息、连接关闭时分别执行对应的操作。接着,我们定义了 `startHeartbeat` 函数,用于定时发送心跳包。在 `onMounted` 生命周期钩子中,我们调用了 `connectWebSocket` 和 `startHeartbeat` 函数。在 `onUnmounted` 生命周期钩子中,我们关闭了 WebSocket 连接。最后,我们将 `message` 变量返回给模板使用。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值