Websockt断线重连和心跳机制

Websockt断线重连和心跳机制

WebSocket心跳机制原理

1.客户端建立WebSocket连接。

2.客户端服务器发送心跳数据包,服务器接收并返回一个表示接收到心跳数据包的响应。

3.当服务器没有及时接收到客户端发送的心跳数据包时,服务器会发送一个关闭连接的请求。

4.服务器定时向客户端发送心跳数据包,客户端接收并返回一个表示接收到心跳数据包的响应。

5.当客户端没有及时接收到服务器发送的心跳数据包时,客户端会重新连接WebSocket

现代浏览器已经不需要前端写心跳机制

其实websocket协议里头,是有控制帧的,就是ping,pong

协议规定,连接两端,一端发送了Ping帧, 那么接收方必须尽快的回复Pong帧数据

参考这个

The Ping frame contains an opcode of 0x9.

The Pong frame contains an opcode of 0xA.

chrome是实现了ping/pong的,只要服务端发送了ping, 那么会立即收到一个pong ,以chrome浏览器举例,只要服务器发送ping消息,浏览器会自动返回一个pong消息。反之也一样。这就是浏览器自带的心跳机制。

各位可以参考下面这个代码自行测试一下:

js 复制代码
const WebSocket = require("ws");

const HEARTBEAT_INTERVAL = 5000; // 心跳间隔(毫秒)
const CLIENT_TIMEOUT = 15000; // 客户端超时时间(毫秒)

const wss = new WebSocket.Server({ port: 3000 });

wss.on("connection", function connection(ws) {
  console.log("Client connected");

  let heartbeatInterval;

  let messageCounter = 0;
  const intervalId = setInterval(() => {
    if (ws.readyState === WebSocket.OPEN) {
      const message = JSON.stringify({ type: "msg", text: messageCounter });
      ws.send(message);
      messageCounter++;
    } else {
      clearInterval(intervalId);
    }
  }, 2000); // 每2秒发送一条消息

  // 客户端心跳检测
  function heartbeat() {
    clearInterval(heartbeatInterval);
    heartbeatInterval = setInterval(() => {
      if (ws.isAlive === false) {
        // console.log("Client is not responding. Attempting to reconnect...");
        console.log("Client is not responding.");
        // 如果客户端由于网络,宕机等原因停止,服务端就停止wsocket连接
        return ws.terminate();
      } else {
        // 如果客户端仍然存活,发送心跳消息
        ws.isAlive = false;
        ws.ping();
        console.log("ping~~");
      }
    }, HEARTBEAT_INTERVAL);
  }

  ws.isAlive = true;

  // 监听来自客户端的消息
  ws.on("message", function incoming(message) {
    console.log("received: %s", message);

    // 解析消息
    let parsedMessage;
    try {
      parsedMessage = JSON.parse(message);
    } catch (error) {
      console.error("Error parsing message:", error);
      return;
    }

    // 处理消息(示例:如果收到的是心跳消息,则更新客户端状态)
    if (parsedMessage.type === "heartbeat") {
      // console.log("Received heartbeat");
      ws.isAlive = true;
      return;
    }

    // 发送消息给客户端
    ws.send(JSON.stringify({ type: "chat", text: parsedMessage.text }));
  });

  // 监听心跳消息
  ws.on("pong", function heartbeatResponse() {
    console.log("pong received");
    ws.isAlive = true;
  });

  // 启动心跳检测
  heartbeat();

  // 监听连接关闭事件
  ws.on("close", function close() {
    console.log("Client disconnected");
    clearInterval(heartbeatInterval);
    clearInterval(intervalId);
  });
});

console.log("WebSocket server running at ws://localhost:3000/");

用浏览器连接一下本机的ws, new WebSocket('http://localhost:3000')

html 复制代码
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>WebSocket Client</title>
  </head>
  <style>
    #messages {
      height: 300px;
      width: 612px;
      overflow: auto;
      border: 1px dashed #ccc;
    }
  </style>
  <body>
    <div id="messages"></div>
    <input type="text" id="messageInput" placeholder="Enter message" />
    <button onclick="sendMessage()">Send</button>
    <button onclick="closeWebSocket()">Close WebSocket</button>
    <button onclick="toggleReconnect()">Toggle Heartbeat</button>
    <button onclick="openWebSocket()">Open WebSocket</button>

    <script>
      const messagesDiv = document.getElementById("messages");
      let ws;
      let heartbeatInterval;
      let shouldReconnect = true;

      function connectWebSocket() {
        ws = new WebSocket("ws://localhost:3000");

        ws.addEventListener("open", function (event) {
          console.log("Connected to WebSocket server");
          messagesDiv.innerHTML += "<p>Connected to WebSocket server</p>";
          startHeartbeat();
        });

        ws.addEventListener("message", function (event) {
          console.log("Received message:", event.data);
          const message = JSON.parse(event.data);
          if (message.type === "chat") {
            messagesDiv.innerHTML +=
              "<p>Received message: " + message.text + "</p>";
            resetHeartbeat();
          }
          //   else if (message.type === "reconnect") {
          //     console.log(
          //       "Received reconnect signal from server. Reconnecting..."
          //     );
          //     reconnectWebSocket();
          //   }
        });

        ws.addEventListener("close", function (event) {
          console.log("Disconnected from WebSocket server");
          messagesDiv.innerHTML += "<p>Disconnected from WebSocket server</p>";
          stopHeartbeat();
          if (shouldReconnect) {
            reconnectWebSocket();
          }
        });

        ws.addEventListener("error", function (event) {
          console.error("WebSocket error:", event);
          // 可以在这里处理错误,例如无法连接到服务器、连接中断等,以及WebSocket API本身的一些限制和错误。
        });
      }

      function sendMessage() {
        const message = document.getElementById("messageInput").value;
        ws.send(JSON.stringify({ type: "chat", text: message }));
        console.log("Sent message: " + message);
        messagesDiv.innerHTML += "<p>Sent message: " + message + "</p>";
      }

      function startHeartbeat() {
        heartbeatInterval = setInterval(function () {
          if (ws.readyState === WebSocket.OPEN) {
            ws.send(JSON.stringify({ type: "heartbeat" }));
            console.log("Sent heartbeat");
          } else {
            console.log("WebSocket connection is not open");
          }
        }, 5000);
      }

      function resetHeartbeat() {
        clearInterval(heartbeatInterval);
        if (shouldReconnect) {
          startHeartbeat();
        }
      }

      function stopHeartbeat() {
        clearInterval(heartbeatInterval);
      }

      function closeWebSocket() {
        shouldReconnect = false; // 关闭 WebSocket 连接后取消重连
        ws.close();
      }

      function toggleReconnect() {
        shouldReconnect = !shouldReconnect; // 切换重连选项
        console.log("Reconnect is", shouldReconnect ? "enabled" : "disabled");
        if (!shouldReconnect) {
          stopHeartbeat();
        } else {
          startHeartbeat();
        }
      }

      function openWebSocket() {
        shouldReconnect = true; // 打开 WebSocket 连接时允许重连
        connectWebSocket();
      }

      function reconnectWebSocket() {
        console.log("Reconnecting to WebSocket server...");
        setTimeout(function () {
          connectWebSocket();
        }, 2000);
      }

      // 初始化时连接 WebSocket
      connectWebSocket();
    </script>
  </body>
</html>

然后就可以看到服务端打印出来

然后你在chrome里面是看不见这个消息的,js代码也不需要任何处理,服务端不停的ping,客户端就会自动pong回来.

相关推荐
半桶水专家6 小时前
用go实现创建WebSocket服务器
服务器·websocket·golang
FeelTouch Labs6 小时前
Netty实现WebSocket Server是否开启压缩深度分析
网络·websocket·网络协议
代码魔法师Sunny3 天前
4.WebSocket 配置与Nginx 的完美结合
websocket·网络协议
kunkun1013 天前
关于Websocket
网络·websocket·网络协议
flying robot4 天前
websocket的使用
websocket
azheng2225 天前
WebSocket消息帧的组成结构
websocket
._Ha!n.5 天前
WebSocket实现消息实时推送
网络·websocket·网络协议
蜀中孤鹰6 天前
由浅入深逐步理解spring boot中如何实现websocket
spring boot·后端·websocket
测试界的酸菜鱼6 天前
C# 如何处理 WebSocket 连接异常
开发语言·websocket·c#
布兰妮甜6 天前
WebSocket详解:从前端到后端的全栈理解
前端·websocket·网络协议