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:
- Server not started or wrong port
- Firewall blocking connection
- Using wrong protocol (ws vs wss)
- Proxy server doesn't support WebSocket
Troubleshooting Steps:
javascriptconst 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:
- Origin verification failed
- Subprotocol mismatch
- 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:
- Connection disconnects during message transmission
- Message queue overflow
- 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:
javascriptclass 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
- Comprehensive Error Handling: Catch all possible errors
- Detailed Logging: Record key events and error information
- Monitoring and Alerting: Monitor server status in real-time
- Stress Testing: Test system performance under high load
- Regular Checks: Regularly check logs and performance metrics
- Documentation: Record known issues and solutions