6月1日 09:11

What are common WebSocket issues? How to troubleshoot and solve them?

In actual development, WebSocket may encounter various problems. Understanding common issues and their solutions is crucial for quickly locating and resolving problems.

Connection Establishment Issues

Issue 1: Connection Cannot Be Established

Symptoms: WebSocket connection stays in CONNECTING state, or reports error directly.

Possible Causes:

  1. Server not started or wrong port
  2. Firewall blocking connection
  3. Using wrong protocol (ws vs wss)
  4. Proxy server doesn't support WebSocket

Troubleshooting Steps:

javascript
const ws = new WebSocket('ws://example.com:8080/socket'); ws.onerror = (error) => { console.error('Connection error:', error); // Check connection state console.log('Connection state:', ws.readyState); // Check network connection if (!navigator.onLine) { console.error('Network not connected'); } }; ws.onclose = (event) => { console.error('Connection closed:', event.code, event.reason); // Determine cause based on close code switch (event.code) { case 1000: console.log('Normal closure'); break; case 1006: console.error('Connection closed abnormally, possibly network issue'); break; case 1002: console.error('Protocol error'); break; } };

Solutions:

javascript
// 1. Verify server address and port function validateServerUrl(url) { try { const parsed = new URL(url); if (!['ws:', 'wss:'].includes(parsed.protocol)) { throw new Error('Protocol error, must use ws or wss'); } if (!parsed.port) { console.warn('Port not specified, using default port'); } return true; } catch (error) { console.error('URL format error:', error); return false; } } // 2. Add timeout handling function connectWithTimeout(url, timeout = 5000) { return new Promise((resolve, reject) => { const ws = new WebSocket(url); const timer = setTimeout(() => { ws.close(); reject(new Error('Connection timeout')); }, timeout); ws.onopen = () => { clearTimeout(timer); resolve(ws); }; ws.onerror = (error) => { clearTimeout(timer); reject(error); }; }); } // 3. Check network status function checkNetworkStatus() { if (!navigator.onLine) { throw new Error('Network not connected'); } // Listen for network status changes window.addEventListener('online', () => { console.log('Network recovered'); }); window.addEventListener('offline', () => { console.log('Network disconnected'); }); }

Issue 2: Handshake Failure

Symptoms: Connection closes immediately after establishment, status code 1002 or 1008.

Possible Causes:

  1. Origin verification failed
  2. Subprotocol mismatch
  3. Authentication failed

Troubleshooting Steps:

javascript
// Server-side logging wss.on('connection', (ws, request) => { console.log('Received connection request'); console.log('Origin:', request.headers.origin); console.log('User-Agent:', request.headers['user-agent']); console.log('Subprotocols:', request.headers['sec-websocket-protocol']); }); // Client-side check const ws = new WebSocket('ws://example.com/socket', ['chat', 'notification']); ws.onclose = (event) => { console.error('Connection close code:', event.code); console.error('Close reason:', event.reason); if (event.code === 1008) { console.error('Authentication failed, please check token'); } };

Solutions:

javascript
// Server-side: Detailed error handling wss.on('connection', (ws, request) => { try { // Verify Origin const origin = request.headers.origin; if (!allowedOrigins.includes(origin)) { ws.close(1008, 'Origin not allowed'); return; } // Verify subprotocol const protocols = request.headers['sec-websocket-protocol']; if (!protocols || !allowedProtocols.some(p => protocols.includes(p))) { ws.close(1002, 'Protocol not supported'); return; } // Verify authentication const token = getTokenFromRequest(request); if (!verifyToken(token)) { ws.close(1008, 'Authentication failed'); return; } // Connection successful console.log('Connection established successfully'); } catch (error) { console.error('Connection handling error:', error); ws.close(1011, 'Internal server error'); } });

Message Transmission Issues

Issue 3: Message Loss

Symptoms: Sent messages don't reach server, or server-sent messages don't reach client.

Possible Causes:

  1. Connection disconnects during message transmission
  2. Message queue overflow
  3. Network latency or packet loss

Troubleshooting Steps:

javascript
// Client-side: Add message acknowledgment mechanism class ReliableWebSocket { constructor(url) { this.ws = new WebSocket(url); this.pendingMessages = new Map(); this.messageId = 0; this.setupMessageHandlers(); } send(data) { const id = ++this.messageId; const message = { id, data, timestamp: Date.now() }; this.pendingMessages.set(id, { message, sendTime: Date.now(), retryCount: 0 }); this.ws.send(JSON.stringify(message)); // Set timeout retry setTimeout(() => { this.checkMessageDelivery(id); }, 5000); } setupMessageHandlers() { this.ws.onmessage = (event) => { const data = JSON.parse(event.data); if (data.type === 'ack') { this.handleAck(data.id); } }; } handleAck(id) { const pending = this.pendingMessages.get(id); if (pending) { const deliveryTime = Date.now() - pending.sendTime; console.log(`Message ${id} delivered, took ${deliveryTime}ms`); this.pendingMessages.delete(id); } } checkMessageDelivery(id) { const pending = this.pendingMessages.get(id); if (pending && pending.retryCount < 3) { console.log(`Message ${id} not acknowledged, retrying...`); pending.retryCount++; this.ws.send(JSON.stringify(pending.message)); setTimeout(() => { this.checkMessageDelivery(id); }, 5000); } else if (pending) { console.error(`Message ${id} send failed`); this.pendingMessages.delete(id); } } }

Issue 4: Message Order Disruption

Symptoms: Later-sent messages arrive first, causing incorrect message processing order.

Solutions:

javascript
class OrderedWebSocket { constructor(url) { this.ws = new WebSocket(url); this.expectedSequence = 0; this.messageBuffer = new Map(); this.setupMessageHandlers(); } setupMessageHandlers() { this.ws.onmessage = (event) => { const data = JSON.parse(event.data); this.handleMessage(data); }; } handleMessage(data) { if (data.sequence === undefined) { // No sequence number, process directly this.processMessage(data); return; } if (data.sequence === this.expectedSequence) { // Expected message, process directly this.processMessage(data); this.expectedSequence++; // Check if there are subsequent messages in buffer this.checkBuffer(); } else if (data.sequence > this.expectedSequence) { // Subsequent message, put in buffer this.messageBuffer.set(data.sequence, data); console.log(`Message ${data.sequence} buffered, waiting for ${this.expectedSequence}`); } else { // Expired message, ignore console.log(`Ignore expired message ${data.sequence}`); } } checkBuffer() { while (this.messageBuffer.has(this.expectedSequence)) { const data = this.messageBuffer.get(this.expectedSequence); this.processMessage(data); this.messageBuffer.delete(this.expectedSequence); this.expectedSequence++; } } processMessage(data) { console.log(`Processing message ${data.sequence}:`, data.content); } }

Performance Issues

Issue 5: Too Many Connections Causing Server Crash

Symptoms: Server CPU and memory usage too high, unable to handle new connections.

Solutions:

javascript
// Server-side: Connection limit const MAX_CONNECTIONS = 10000; const MAX_CONNECTIONS_PER_IP = 10; const connectionCount = new Map(); const ipConnectionCount = new Map(); wss.on('connection', (ws, request) => { const ip = request.socket.remoteAddress; // Check total connections if (connectionCount.size >= MAX_CONNECTIONS) { ws.close(1008, 'Server full'); return; } // Check single IP connections const ipCount = ipConnectionCount.get(ip) || 0; if (ipCount >= MAX_CONNECTIONS_PER_IP) { ws.close(1008, 'Too many connections from this IP'); return; } // Record connection connectionCount.set(ws, ip); ipConnectionCount.set(ip, ipCount + 1); ws.on('close', () => { const clientIp = connectionCount.get(ws); if (clientIp) { const count = ipConnectionCount.get(clientIp) - 1; if (count <= 0) { ipConnectionCount.delete(clientIp); } else { ipConnectionCount.set(clientIp, count); } connectionCount.delete(ws); } }); }); // Periodically clean up zombie connections setInterval(() => { wss.clients.forEach((ws) => { if (ws.readyState === WebSocket.OPEN) { // Send ping to detect connection ws.ping(); } else { // Close invalid connection ws.terminate(); } }); }, 30000);

Issue 6: Memory Leak

Symptoms: Server memory usage keeps growing, eventually leading to OOM.

Troubleshooting Steps:

javascript
// Add memory monitoring setInterval(() => { const used = process.memoryUsage(); console.log('Memory usage:'); console.log(` RSS: ${Math.round(used.rss / 1024 / 1024)} MB`); console.log(` Heap Total: ${Math.round(used.heapTotal / 1024 / 1024)} MB`); console.log(` Heap Used: ${Math.round(used.heapUsed / 1024 / 1024)} MB`); console.log(` External: ${Math.round(used.external / 1024 / 1024)} MB`); }, 60000); // Check connection count setInterval(() => { console.log('Current connections:', wss.clients.size); }, 60000);

Solutions:

javascript
// 1. Clean up event listeners in time ws.on('close', () => { // Clean up all event listeners ws.removeAllListeners(); // Clean up related data if (ws.userData) { delete ws.userData; } }); // 2. Limit message buffer size const MAX_BUFFER_SIZE = 100; wss.on('connection', (ws) => { ws.messageBuffer = []; ws.on('message', (message) => { ws.messageBuffer.push(message); // Limit buffer size if (ws.messageBuffer.length > MAX_BUFFER_SIZE) { ws.messageBuffer.shift(); } }); ws.on('close', () => { // Clean up buffer ws.messageBuffer = null; }); }); // 3. Use WeakMap for temporary data const weakMap = new WeakMap(); wss.on('connection', (ws) => { weakMap.set(ws, { timestamp: Date.now() }); ws.on('close', () => { // WeakMap will automatically clean up }); });

Debugging Techniques

1. Enable Detailed Logging

javascript
// Server-side const DEBUG = process.env.NODE_ENV !== 'production'; function debugLog(...args) { if (DEBUG) { console.log('[DEBUG]', ...args); } } wss.on('connection', (ws, request) => { debugLog('New connection:', request.socket.remoteAddress); ws.on('message', (message) => { debugLog('Received message:', message.toString()); }); ws.on('close', (code, reason) => { debugLog('Connection closed:', code, reason.toString()); }); });

2. Use Browser Developer Tools

javascript
// Monitor WebSocket in browser const ws = new WebSocket('ws://example.com/socket'); // Intercept all WebSocket events const originalSend = ws.send.bind(ws); ws.send = function(data) { console.log('Send:', data); return originalSend(data); }; ws.onmessage = (event) => { console.log('Receive:', event.data); };

3. Use Network Packet Capture Tools

  • Wireshark: Capture network packets, analyze WebSocket communication
  • Chrome DevTools: View WebSocket frames in Network tab
  • Fiddler: Intercept and debug WebSocket traffic

Best Practices

  1. Comprehensive Error Handling: Catch all possible errors
  2. Detailed Logging: Record key events and error information
  3. Monitoring and Alerting: Monitor server status in real-time
  4. Stress Testing: Test system performance under high load
  5. Regular Checks: Regularly check logs and performance metrics
  6. Documentation: Record known issues and solutions
标签:WebSocket