WebSocket连接状态监控与自动重连实现
下面我将实现一个具有连接状态监控和自动重连功能的WebSocket聊天室界面。
设计思路
- 创建直观的连接状态指示器
- 实现自动重连机制,包括:
- 指数退避策略(重连间隔逐渐增加)
- 最大重连次数限制
- 手动重连按钮
- 添加心跳检测机制确保连接活跃
- 提供清晰的用户反馈
完整代码实现
<!DOCTYPE html>
<html lang="zh-CN">
<head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>WebSocket聊天室 - 带自动重连</title><style>* {box-sizing: border-box;margin: 0;padding: 0;}body {font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;line-height: 1.6;color: #333;background: linear-gradient(135deg, #f5f7fa 0%, #c3cfe2 100%);min-height: 100vh;padding: 20px;display: flex;justify-content: center;align-items: center;}.container {width: 100%;max-width: 800px;background: white;border-radius: 12px;box-shadow: 0 10px 30px rgba(0, 0, 0, 0.15);overflow: hidden;}header {background: #4a6ee0;color: white;padding: 20px;text-align: center;position: relative;}h1 {font-size: 24px;margin-bottom: 5px;}.subtitle {font-size: 14px;opacity: 0.9;}.status-bar {display: flex;justify-content: space-between;align-items: center;padding: 12px 20px;background: #f0f4ff;border-bottom: 1px solid #e0e6ff;}.status {display: flex;align-items: center;gap: 10px;}.status-indicator {width: 12px;height: 12px;border-radius: 50%;}.status.connected .status-indicator {background: #4caf50;box-shadow: 0 0 8px #4caf50;}.status.connecting .status-indicator {background: #ff9800;animation: pulse 1.5s infinite;}.status.disconnected .status-indicator {background: #f44336;}.reconnect-info {font-size: 13px;color: #666;margin-left: 15px;}.chat-container {padding: 20px;height: 350px;overflow-y: auto;background: #fafbff;display: flex;flex-direction: column;}.message {margin-bottom: 15px;padding: 10px 15px;border-radius: 18px;max-width: 80%;word-break: break-word;position: relative;}.received {background: #e6eeff;border-top-left-radius: 4px;align-self: flex-start;}.sent {background: #4a6ee0;color: white;border-top-right-radius: 4px;margin-left: auto;}.system-message {align-self: center;background: #f0f4ff;color: #666;font-style: italic;text-align: center;max-width: 90%;font-size: 14px;}.message-input {display: flex;padding: 15px;border-top: 1px solid #e0e6ff;background: #fafbff;}input {flex: 1;padding: 12px 15px;border: 1px solid #d0d9ff;border-radius: 20px;outline: none;font-size: 16px;transition: border-color 0.3s;}input:focus {border-color: #4a6ee0;box-shadow: 0 0 0 2px rgba(74, 110, 224, 0.2);}button {background: #4a6ee0;color: white;border: none;border-radius: 20px;padding: 12px 20px;margin-left: 10px;cursor: pointer;font-weight: bold;transition: all 0.3s;}button:hover {background: #3a5ec0;transform: translateY(-2px);}button:active {transform: translateY(0);}button:disabled {background: #cccccc;cursor: not-allowed;transform: none;}.reconnect-button {background: #ff9800;}.reconnect-button:hover {background: #f57c00;}@keyframes pulse {0% { opacity: 1; }50% { opacity: 0.5; }100% { opacity: 1; }}.connection-stats {display: flex;padding: 10px 20px;background: #f8f9ff;border-top: 1px solid #e0e6ff;font-size: 13px;color: #666;justify-content: space-between;}</style>
</head>
<body><div class="container"><header><h1>WebSocket聊天室</h1><div class="subtitle">带自动重连和连接状态监控</div></header><div class="status-bar"><div class="status disconnected" id="status"><div class="status-indicator"></div><span id="statusText">连接已断开</span><span class="reconnect-info" id="reconnectInfo"></span></div><button id="connectButton" class="reconnect-button">连接</button></div><div class="chat-container" id="chatContainer"><div class="message system-message">欢迎使用WebSocket聊天室!点击"连接"按钮开始。</div></div><div class="message-input"><input type="text" id="messageInput" placeholder="输入消息..." disabled><button id="sendButton" disabled>发送</button></div><div class="connection-stats"><span id="connectionStats">连接次数: 0 | 消息数: 0</span><span id="latencyInfo">延迟: - ms</span></div></div><script>document.addEventListener('DOMContentLoaded', function() {const statusElement = document.getElementById('status');const statusText = document.getElementById('statusText');const reconnectInfo = document.getElementById('reconnectInfo');const connectButton = document.getElementById('connectButton');const chatContainer = document.getElementById('chatContainer');const messageInput = document.getElementById('messageInput');const sendButton = document.getElementById('sendButton');const connectionStats = document.getElementById('connectionStats');const latencyInfo = document.getElementById('latencyInfo');// WebSocket和状态变量let websocket = null;let reconnectAttempts = 0;const maxReconnectAttempts = 10;let reconnectTimer = null;let connectionCount = 0;let messageCount = 0;let heartbeatInterval = null;let lastHeartbeat = null;// 使用公共的WebSocket测试服务器const serverUrl = 'wss://echo.websocket.org';// 更新连接状态UIfunction updateConnectionStatus(state, message = '') {statusElement.className = 'status ' + state;switch(state) {case 'connected':statusText.textContent = '已连接';connectButton.textContent = '断开';connectButton.classList.remove('reconnect-button');messageInput.disabled = false;sendButton.disabled = false;reconnectInfo.textContent = '';break;case 'connecting':statusText.textContent = '连接中...';connectButton.textContent = '取消';connectButton.classList.add('reconnect-button');messageInput.disabled = true;sendButton.disabled = true;break;case 'disconnected':statusText.textContent = message || '连接已断开';connectButton.textContent = '重连';connectButton.classList.add('reconnect-button');messageInput.disabled = true;sendButton.disabled = true;break;}updateStats();}// 更新统计信息function updateStats() {connectionStats.textContent = `连接次数: ${connectionCount} | 消息数: ${messageCount}`;}// 添加消息到聊天窗口function addMessage(content, type = 'received') {const messageElement = document.createElement('div');messageElement.className = `message ${type}`;messageElement.textContent = content;chatContainer.appendChild(messageElement);// 滚动到底部chatContainer.scrollTop = chatContainer.scrollHeight;if (type === 'sent' || type === 'received') {messageCount++;updateStats();}}// 计算下一次重连的延迟(指数退避)function getReconnectDelay() {const baseDelay = 1000; // 1秒基础延迟const maxDelay = 30000; // 30秒最大延迟const delay = Math.min(maxDelay, baseDelay * Math.pow(1.5, reconnectAttempts));// 添加随机性避免所有客户端同时重连return delay + Math.random() * 1000;}// 建立WebSocket连接function connect() {// 清除现有的重连计时器if (reconnectTimer) {clearTimeout(reconnectTimer);reconnectTimer = null;}// 如果已有连接,先关闭if (websocket && websocket.readyState === WebSocket.OPEN) {disconnect();return;}updateConnectionStatus('connecting');try {websocket = new WebSocket(serverUrl);websocket.onopen = function(event) {connectionCount++;reconnectAttempts = 0;updateConnectionStatus('connected');addMessage('已成功连接到服务器', 'system-message');// 启动心跳检测startHeartbeat();};websocket.onmessage = function(event) {// 检查是否是心跳响应if (event.data === 'pong') {const now = new Date().getTime();const latency = now - lastHeartbeat;latencyInfo.textContent = `延迟: ${latency} ms`;return;}addMessage(event.data);};websocket.onclose = function(event) {// 清除心跳检测stopHeartbeat();addMessage(`连接关闭: ${event.code} ${event.reason || '无原因'}`, 'system-message');// 如果不是手动断开,尝试重连if (reconnectAttempts < maxReconnectAttempts) {reconnectAttempts++;const delay = getReconnectDelay();reconnectInfo.textContent = `尝试 ${reconnectAttempts}/${maxReconnectAttempts}, 下次尝试: ${Math.round(delay/1000)}秒`;reconnectTimer = setTimeout(connect, delay);updateConnectionStatus('disconnected', '连接断开,正在尝试重连...');} else {updateConnectionStatus('disconnected', '连接断开,已达最大重试次数');reconnectInfo.textContent = '';}};websocket.onerror = function(error) {addMessage('连接发生错误', 'system-message');console.error('WebSocket错误:', error);};} catch (error) {console.error('无法建立连接:', error);addMessage('无法连接到服务器: ' + error.message, 'system-message');updateConnectionStatus('disconnected');}}// 关闭WebSocket连接function disconnect() {// 清除重连计时器if (reconnectTimer) {clearTimeout(reconnectTimer);reconnectTimer = null;}// 清除心跳检测stopHeartbeat();if (websocket) {websocket.close();websocket = null;}updateConnectionStatus('disconnected', '连接已断开');reconnectAttempts = 0;reconnectInfo.textContent = '';}// 发送消息function sendMessage() {const message = messageInput.value.trim();if (message && websocket && websocket.readyState === WebSocket.OPEN) {websocket.send(message);addMessage(message, 'sent');messageInput.value = '';}}// 启动心跳检测function startHeartbeat() {// 每10秒发送一次心跳heartbeatInterval = setInterval(() => {if (websocket && websocket.readyState === WebSocket.OPEN) {lastHeartbeat = new Date().getTime();websocket.send('ping');}}, 10000);}// 停止心跳检测function stopHeartbeat() {if (heartbeatInterval) {clearInterval(heartbeatInterval);heartbeatInterval = null;}}// 连接/断开按钮点击事件connectButton.addEventListener('click', function() {if (websocket && websocket.readyState === WebSocket.OPEN) {disconnect();} else if (reconnectTimer) {// 取消重连clearTimeout(reconnectTimer);reconnectTimer = null;reconnectAttempts = maxReconnectAttempts; // 阻止自动重连updateConnectionStatus('disconnected', '连接已取消');reconnectInfo.textContent = '';} else {connect();}});// 发送按钮点击事件sendButton.addEventListener('click', sendMessage);// 按Enter键发送消息messageInput.addEventListener('keypress', function(event) {if (event.key === 'Enter') {sendMessage();}});// 初始状态updateConnectionStatus('disconnected');});</script>
</body>
</html>
功能说明
这个实现包含以下核心功能:
-
连接状态监控:
- 直观的视觉指示器(绿色=已连接,橙色=连接中,红色=已断开)
- 详细的连接状态文本信息
-
自动重连机制:
- 使用指数退避算法,重试间隔逐渐增加
- 最大重试次数限制(10次)
- 显示下一次重连的剩余时间
-
心跳检测:
- 每10秒发送一次ping消息
- 计算并显示网络延迟
-
连接统计:
- 显示连接次数和消息数量
- 实时延迟显示
-
用户控制:
- 可以手动连接/断开
- 可以取消自动重连过程
使用说明
- 页面加载后,点击"连接"按钮建立WebSocket连接
- 连接成功后,可以在输入框中输入消息并发送
- 如果连接断开,系统会自动尝试重连
- 可以随时点击"断开"按钮结束连接,或点击"重连"按钮手动重新连接
这个实现提供了健壮的WebSocket连接管理,适合在生产环境中使用。