WebSocket Reconnection Mechanism Explained
WebSocket connections may disconnect due to network fluctuations, server restarts, timeouts, etc. Implementing a reliable reconnection mechanism is crucial.
Connection State Detection
WebSocket has four connection states:
javascriptconst ws = new WebSocket('ws://example.com'); // 0: CONNECTING - Connecting // 1: OPEN - Connected // 2: CLOSING - Closing // 3: CLOSED - Closed console.log(ws.readyState); // Get current state
Basic Reconnection Implementation
javascriptclass WebSocketManager { constructor(url) { this.url = url; this.ws = null; this.reconnectAttempts = 0; this.maxReconnectAttempts = 5; this.reconnectInterval = 3000; // 3 seconds this.shouldReconnect = true; this.connect(); } connect() { this.ws = new WebSocket(this.url); this.ws.onopen = () => { console.log('WebSocket connection established'); this.reconnectAttempts = 0; }; this.ws.onclose = (event) => { console.log('WebSocket connection closed:', event.code, event.reason); if (this.shouldReconnect) { this.reconnect(); } }; this.ws.onerror = (error) => { console.error('WebSocket error:', error); }; } reconnect() { if (this.reconnectAttempts >= this.maxReconnectAttempts) { console.error('Max reconnection attempts reached, stop reconnecting'); return; } this.reconnectAttempts++; const delay = this.getReconnectDelay(); console.log(`Attempting ${this.reconnectAttempts}th reconnection after ${delay}ms`); setTimeout(() => { this.connect(); }, delay); } getReconnectDelay() { // Exponential backoff strategy return Math.min( this.reconnectInterval * Math.pow(2, this.reconnectAttempts - 1), 30000 // Max 30 seconds ); } disconnect() { this.shouldReconnect = false; if (this.ws) { this.ws.close(); } } } // Usage example const wsManager = new WebSocketManager('ws://example.com/socket');
Graceful Close Handling
javascriptclass WebSocketManager { // ... other code close(code = 1000, reason = 'Normal closure') { this.shouldReconnect = false; if (this.ws && this.ws.readyState === WebSocket.OPEN) { this.ws.close(code, reason); } } // Determine if it's a normal close in onclose ws.onclose = (event) => { // Normal close status code: 1000 if (event.code === 1000) { console.log('Normal connection close'); return; } // Abnormal close, try to reconnect if (this.shouldReconnect) { this.reconnect(); } }; }
Common Close Status Codes
| Status Code | Meaning |
|---|---|
| 1000 | Normal closure |
| 1001 | Endpoint going away |
| 1002 | Protocol error |
| 1003 | Unsupported data type |
| 1005 | No status code (internal use) |
| 1006 | Connection closed abnormally |
| 1007 | Data type inconsistency |
| 1008 | Policy violation |
| 1009 | Message too large |
| 1010 | Missing extension |
| 1011 | Internal error |
| 1015 | TLS handshake failure |
Heartbeat Detection and Reconnection
javascriptclass WebSocketManager { constructor(url) { // ... other initialization this.heartbeatInterval = 30000; // 30 seconds this.heartbeatTimer = null; this.pongTimeout = 5000; // 5 second timeout this.pongTimer = null; } startHeartbeat() { this.heartbeatTimer = setInterval(() => { if (this.ws && this.ws.readyState === WebSocket.OPEN) { this.sendPing(); } }, this.heartbeatInterval); } sendPing() { this.ws.send(JSON.stringify({ type: 'ping' })); // Set pong timeout this.pongTimer = setTimeout(() => { console.error('Heartbeat timeout, closing connection'); this.ws.close(); }, this.pongTimeout); } handlePong() { if (this.pongTimer) { clearTimeout(this.pongTimer); this.pongTimer = null; } } ws.onmessage = (event) => { const data = JSON.parse(event.data); if (data.type === 'pong') { this.handlePong(); } // Handle other messages... }; ws.onopen = () => { this.startHeartbeat(); }; ws.onclose = () => { this.stopHeartbeat(); }; stopHeartbeat() { if (this.heartbeatTimer) { clearInterval(this.heartbeatTimer); this.heartbeatTimer = null; } if (this.pongTimer) { clearTimeout(this.pongTimer); this.pongTimer = null; } } }
Offline Detection
javascriptclass WebSocketManager { constructor(url) { // ... other initialization this.setupOfflineDetection(); } setupOfflineDetection() { window.addEventListener('online', () => { console.log('Network recovered, attempting to reconnect'); if (!this.ws || this.ws.readyState !== WebSocket.OPEN) { this.connect(); } }); window.addEventListener('offline', () => { console.log('Network disconnected'); }); } }
Best Practices
- Exponential Backoff: Gradually increase reconnection interval to avoid server pressure
- Max Reconnection Attempts: Prevent infinite reconnection
- Heartbeat Mechanism: Timely detect connection status
- Graceful Close: Distinguish between normal and abnormal closure
- Offline Detection: Listen for network state changes
- Status Notification: Provide connection status feedback to users
- Message Queue: Cache messages during reconnection, send after connection succeeds