HOW - 保证 WebSocket 持续正常连接

一、基于 React 的 WebSocket

下面是一个React版本的WebSocket连接代码示例,展示了如何在React组件中实现WebSocket连接、心跳机制以及自动重连功能。

WebSocketManager.js

首先,我们可以创建一个 WebSocketManager 类来封装WebSocket的逻辑,包括连接、心跳和重连。

javascript 复制代码
// WebSocketManager.js

class WebSocketManager {
  constructor(url) {
    this.url = url;
    this.ws = null;
    // Ping和Pong是websocket里的心跳,用来保证客户端是在线的,一般来说只有服务端给客户端发送Ping,然后客户端发送Pong来回应,表明自己仍然在线
    this.pingInterval = null;
    this.pongTimeout = null;
    
    this.reconnectDelay = 5000; // 5秒后重新连接
  }

  connect() {
    this.ws = new WebSocket(this.url);

    this.ws.onopen = () => {
      console.log('Connected');
      this.startHeartbeat();
    };

    this.ws.onclose = (event) => {
      console.log(`Connection closed: ${event.reason}`);
      clearInterval(this.pingInterval);
      clearTimeout(this.pongTimeout);
      setTimeout(() => this.connect(), this.reconnectDelay);
    };

    this.ws.onerror = (error) => {
      console.error(`WebSocket error: ${error.message}`);
    };

    this.ws.onmessage = (event) => {
      const message = JSON.parse(event.data);
      if (message.type === 'pong') {
        console.log('Received pong');
        clearTimeout(this.pongTimeout);
      } else {
        this.handleMessage(message);
      }
    };
  }

  startHeartbeat() {
    this.pingInterval = setInterval(() => {
      if (this.ws.readyState === WebSocket.OPEN) {
        this.ws.send(JSON.stringify({ type: 'ping' }));
        this.pongTimeout = setTimeout(() => {
          console.warn('Pong timeout, closing connection');
          this.ws.close();
        }, 5000);
      }
    }, 30000);
  }

  handleMessage(message) {
    // 处理接收到的其他消息
    console.log('Received message:', message);
  }

  send(data) {
    if (this.ws.readyState === WebSocket.OPEN) {
      this.ws.send(JSON.stringify(data));
    }
  }
}

export default WebSocketManager;

App.js

然后,在React组件中使用这个 WebSocketManager

javascript 复制代码
// App.js

import React, { useEffect } from 'react';
import WebSocketManager from './WebSocketManager';

const App = () => {
  useEffect(() => {
    const wsManager = new WebSocketManager('ws://example.com/socket');
    wsManager.connect();

    // 清理函数,组件卸载时关闭 WebSocket 连接
    return () => {
      if (wsManager.ws) {
        clearInterval(wsManager.pingInterval);
        clearTimeout(wsManager.pongTimeout);
        wsManager.ws.close();
      }
    };
  }, []);

  return (
    <div className="App">
      <h1>WebSocket Demo</h1>
      {/* 其他组件或内容 */}
    </div>
  );
};

export default App;

解释

  1. WebSocketManager 类

    • 封装 WebSocket 连接的逻辑,包含连接、心跳和重连功能。
    • connect 方法用于建立 WebSocket 连接并设置相应的事件处理器。
    • startHeartbeat 方法实现心跳机制,每隔30秒发送一次 ping 消息,如果5秒内没有收到 pong 响应,则关闭连接并触发重连。
    • handleMessage 方法处理接收到的其他消息,可以根据需要进行扩展。
    • send 方法用于发送消息。
  2. App 组件

    • 使用 useEffect 在组件挂载时创建 WebSocketManager 实例并建立连接,在组件卸载时清理定时器和关闭连接。

通过这种方式,可以在 React 项目中实现稳定的 WebSocket 连接,并具备自动重连和心跳检测功能。

注意:避免无限制重连

上面的示例中,客户端会一直尝试重连。每当连接关闭时,都会在5秒后重新尝试连接。这样可以确保在网络暂时中断或服务器暂时不可用的情况下,客户端能够自动恢复连接。

如果希望更精细地控制重连行为,例如限制重连次数或逐步增加重连间隔,可以进行以下改进:

  1. 限制重连次数

    • 可以增加一个变量来记录重连次数,并在超过最大重连次数后停止重连。
  2. 逐步增加重连间隔

    • 可以使用指数退避(exponential backoff)算法来逐步增加重连间隔,以避免频繁重连。

下面是改进后的 WebSocketManager 类:

javascript 复制代码
// WebSocketManager.js

class WebSocketManager {
  constructor(url) {
    this.url = url;
    this.ws = null;
    this.pingInterval = null;
    this.pongTimeout = null;
    this.reconnectDelay = 5000; // 初始重连延迟
    this.maxReconnectDelay = 30000; // 最大重连延迟
    this.reconnectAttempts = 0; // 重连次数
    this.maxReconnectAttempts = 10; // 最大重连次数
  }

  connect() {
    this.ws = new WebSocket(this.url);

    this.ws.onopen = () => {
      console.log('Connected');
      this.reconnectAttempts = 0; // 重置重连次数
      this.reconnectDelay = 5000; // 重置重连延迟
      this.startHeartbeat();
    };

    this.ws.onclose = (event) => {
      console.log(`Connection closed: ${event.reason}`);
      clearInterval(this.pingInterval);
      clearTimeout(this.pongTimeout);

      if (this.reconnectAttempts < this.maxReconnectAttempts) {
        this.reconnectAttempts++;
        setTimeout(() => this.connect(), this.reconnectDelay);
        this.reconnectDelay = Math.min(this.reconnectDelay * 2, this.maxReconnectDelay); // 指数退避
      } else {
        console.log('Max reconnect attempts reached. Giving up.');
      }
    };

    this.ws.onerror = (error) => {
      console.error(`WebSocket error: ${error.message}`);
    };

    this.ws.onmessage = (event) => {
      const message = JSON.parse(event.data);
      if (message.type === 'pong') {
        console.log('Received pong');
        clearTimeout(this.pongTimeout);
      } else {
        this.handleMessage(message);
      }
    };
  }

  startHeartbeat() {
    this.pingInterval = setInterval(() => {
      if (this.ws.readyState === WebSocket.OPEN) {
        this.ws.send(JSON.stringify({ type: 'ping' }));
        this.pongTimeout = setTimeout(() => {
          console.warn('Pong timeout, closing connection');
          this.ws.close();
        }, 5000);
      }
    }, 30000);
  }

  handleMessage(message) {
    // 处理接收到的其他消息
    console.log('Received message:', message);
  }

  send(data) {
    if (this.ws.readyState === WebSocket.OPEN) {
      this.ws.send(JSON.stringify(data));
    }
  }
}

export default WebSocketManager;

解释:

  1. 限制重连次数

    • 增加了 reconnectAttemptsmaxReconnectAttempts 变量,用于记录和限制重连次数。
    • onclose 事件中,检查 reconnectAttempts 是否小于 maxReconnectAttempts,如果是则继续重连,否则停止重连。
  2. 逐步增加重连间隔

    • 使用 reconnectDelaymaxReconnectDelay 变量控制重连延迟。
    • 在每次重连后,使用 Math.min(this.reconnectDelay * 2, this.maxReconnectDelay) 增加重连延迟,直到达到最大重连延迟。

通过这种改进,可以更好地控制重连行为,避免客户端无限制地尝试重连,并在重连失败后逐步增加重连间隔。

当然,这个时间间隔应该最好可以比「服务端重启 Websocket」来的久,这样就可以在服务端出现故障重启时正常重连。

二、为什么会 ping 不通

当客户端一直无法 ping 通服务器时,可能会发生以下几种情况:

  1. 连接断开

    • 如果客户端无法接收到服务器的 pong 响应,可能意味着连接已经断开或存在网络问题。在这种情况下,客户端需要采取措施进行重连。
  2. 服务器无响应

    • 如果服务器因故未能回复 pong 消息,客户端会认为连接已失效。同样需要尝试重连。
  3. 连接超时

    • 为了防止无限等待,客户端可以设置一个超时时间。如果在指定时间内没有收到 pong 响应,就认为连接失效并进行重连。

三、保证持续正常连接的最佳实践

保证 WebSocket 持续正常连接可以通过以下几个步骤和最佳实践:

2.1 定时发送心跳消息 (Heartbeat/Ping-Pong)

定时发送心跳消息来保持连接活跃。服务器和客户端可以定期发送 Ping 和 Pong 消息,以确保连接未被中断。

javascript 复制代码
const ws = new WebSocket('ws://example.com/socket');

const interval = setInterval(() => {
  if (ws.readyState === WebSocket.OPEN) {
    ws.send(JSON.stringify({ type: 'ping' }));
  }
}, 30000); // 每30秒发送一次心跳消息

ws.onmessage = (event) => {
  const message = JSON.parse(event.data);
  if (message.type === 'pong') {
    // 处理心跳回复
  }
};

2.2 自动重连机制

当连接断开时,自动尝试重新连接。

javascript 复制代码
let ws;

function connect() {
  ws = new WebSocket('ws://example.com/socket');

  ws.onopen = () => {
    console.log('Connected');
  };

  ws.onclose = (event) => {
    console.log(`Connection closed: ${event.reason}`);
    setTimeout(connect, 5000); // 5秒后重新连接
  };

  ws.onerror = (error) => {
    console.error(`WebSocket error: ${error.message}`);
  };

  ws.onmessage = (event) => {
    // 处理接收到的消息
  };
}

connect();

2.3 检测网络状态

通过监听网络状态变化来处理网络断开和恢复。

javascript 复制代码
window.addEventListener('online', () => {
  console.log('Network is online. Attempting to reconnect...');
  connect();
});

window.addEventListener('offline', () => {
  console.log('Network is offline. Closing WebSocket connection...');
  ws.close();
});

2.4 处理服务器端断开

服务器端可以通过发送特定消息通知客户端即将断开,客户端收到后可进行相应处理。

javascript 复制代码
ws.onmessage = (event) => {
  const message = JSON.parse(event.data);
  if (message.type === 'server-close') {
    console.log('Server is closing the connection');
    ws.close();
  }
};

2.5 增加异常处理

在连接的生命周期内,添加对各种异常情况的处理,以确保程序不会因为未处理的异常而崩溃。

javascript 复制代码
ws.onerror = (error) => {
  console.error(`WebSocket error: ${error.message}`);
  // 可以根据错误类型进行不同的处理,例如重连、提示用户等
};

通过上述方法,可以提高 WebSocket 连接的稳定性和持续性,确保在网络状况良好时,连接可以保持正常。如果遇到网络波动或其他异常情况,也能及时进行恢复和重连。

相关推荐
寻星探路2 小时前
【深度长文】万字攻克网络原理:从 HTTP 报文解构到 HTTPS 终极加密逻辑
java·开发语言·网络·python·http·ai·https
王达舒19942 小时前
HTTP vs HTTPS: 终极解析,保护你的数据究竟有多重要?
网络协议·http·https
朱皮皮呀2 小时前
HTTPS的工作过程
网络协议·http·https
Binary-Jeff2 小时前
一文读懂 HTTPS 协议及其工作流程
网络协议·web安全·http·https
崔庆才丨静觅4 小时前
hCaptcha 验证码图像识别 API 对接教程
前端
passerby60615 小时前
完成前端时间处理的另一块版图
前端·github·web components
掘了5 小时前
「2025 年终总结」在所有失去的人中,我最怀念我自己
前端·后端·年终总结
崔庆才丨静觅5 小时前
实用免费的 Short URL 短链接 API 对接说明
前端
崔庆才丨静觅5 小时前
5分钟快速搭建 AI 平台并用它赚钱!
前端
盟接之桥5 小时前
盟接之桥说制造:引流品 × 利润品,全球电商平台高效产品组合策略(供讨论)
大数据·linux·服务器·网络·人工智能·制造