Skip to content

Websocket Reconnection

DodaTech 5 min read

title: "WebSocket Reconnection" description: "Learn WebSocket reconnection strategies including exponential backoff, jitter, connection state management, and graceful recovery from network interruptions." weight: 22 date: 2026-06-28 lastmod: 2026-06-28 tags: ["apis", "websocket"]


Network interruptions are inevitable. WebSocket reconnection strategies ensure your application recovers gracefully from disconnections without data loss or user-facing errors.

## What You'll Learn

- Why WebSocket reconnection is necessary
- Exponential backoff algorithms
- Adding jitter to prevent thundering herds
- Connection state management
- Reconnection with message replay

## Why It Matters

Without reconnection, a brief network interruption forces users to reload the page and lose context. Proper reconnection provides a seamless user experience even during network instability.

## Real-World Use

A mobile trading app uses exponential backoff with jitter for WebSocket reconnection. When a user enters a subway tunnel, the connection drops and reconnects automatically within seconds, without losing any trade confirmations.

## Flow Chart

```mermaid
stateDiagram-v2
    [*] --> Connected
    Connected --> Disconnected: Network Error
    Disconnected --> Reconnecting: Start Reconnect
    Reconnecting --> Connected: Success
    Reconnecting --> Backoff: Failed
    Backoff --> Reconnecting: Wait Complete
    Backoff --> MaxRetries: Exceeded Limit
    MaxRetries --> [*]: Give Up

Code Examples

Example 1: Reconnection with Exponential Backoff

class ReconnectingWebSocket {
  constructor(url, options = {}) {
    this.url = url;
    this.maxRetries = options.maxRetries || Infinity;
    this.maxDelay = options.maxDelay || 30000;
    this.baseDelay = options.baseDelay || 1000;
    this.retryCount = 0;
    this.connect();
  }

  connect() {
    this.ws = new WebSocket(this.url);
    
    this.ws.onopen = () => {
      console.log('Connected');
      this.retryCount = 0;
      this.onopen?.();
    };

    this.ws.onclose = (event) => {
      if (!event.wasClean) {
        this.reconnect();
      }
      this.onclose?.(event);
    };

    this.ws.onerror = (error) => {
      console.error('WebSocket error:', error);
      // onerror is followed by onclose, so reconnection happens there
    };

    this.ws.onmessage = (event) => {
      this.onmessage?.(event);
    };
  }

  reconnect() {
    if (this.retryCount >= this.maxRetries) {
      console.error('Max retries reached');
      this.onmaxretries?.();
      return;
    }

    const delay = this.getBackoffDelay();
    console.log(`Reconnecting in ${delay}ms (attempt ${this.retryCount + 1})`);
    
    setTimeout(() => {
      this.retryCount++;
      this.connect();
    }, delay);
  }

  getBackoffDelay() {
    // Exponential backoff with full jitter
    const exponentialDelay = Math.min(
      this.baseDelay * Math.pow(2, this.retryCount),
      this.maxDelay
    );
    return Math.random() * exponentialDelay;
  }

  send(data) {
    if (this.ws?.readyState === WebSocket.OPEN) {
      this.ws.send(data);
    } else {
      console.warn('Cannot send, not connected');
    }
  }

  close() {
    this.maxRetries = 0;
    this.ws?.close();
  }
}

// Usage
const ws = new ReconnectingWebSocket('wss://server.example.com/ws', {
  maxRetries: 10,
  baseDelay: 500,
  maxDelay: 15000,
});

ws.onmessage = (event) => {
  console.log('Received:', event.data);
};

ws.onmaxretries = () => {
  console.log('Showing offline message to user');
};

Expected output: Client automatically reconnects with exponential backoff, giving up after 10 attempts.

Example 2: Reconnection with Message Queue

class QueuedReconnectingWebSocket {
  constructor(url) {
    this.url = url;
    this.messageQueue = [];
    this.isConnected = false;
    this.connect();
  }

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

    this.ws.onopen = () => {
      this.isConnected = true;
      this.flushQueue();
      this.onopen?.();
    };

    this.ws.onclose = () => {
      this.isConnected = false;
      setTimeout(() => this.connect(), this.getBackoffDelay());
    };
  }

  flushQueue() {
    while (this.messageQueue.length > 0) {
      const message = this.messageQueue.shift();
      this.ws.send(message);
    }
  }

  send(data) {
    if (this.isConnected) {
      this.ws.send(data);
    } else {
      this.messageQueue.push(data);
      console.log('Queued message for later delivery');
    }
  }

  getBackoffDelay() {
    return 1000 + Math.random() * 4000;
  }
}

// Usage with message tracking
const ws = new QueuedReconnectingWebSocket('wss://server.example.com/ws');

// These messages will be queued if not connected
ws.send(JSON.stringify({ type: 'join', room: 'general' }));
ws.send(JSON.stringify({ type: 'message', text: 'Hello' }));

ws.onopen = () => {
  console.log('Connected! Queued messages flushed.');
};

Expected output: Messages sent while disconnected are queued and automatically sent when the connection is restored.

Example 3: Socket.IO Reconnection Configuration

const { io } = require('socket.io-client');

const socket = io('wss://server.example.com', {
  // Reconnection options
  reconnection: true,
  reconnectionAttempts: 20,
  reconnectionDelay: 1000,
  reconnectionDelayMax: 30000,
  randomizationFactor: 0.5,
  
  // Connection options
  timeout: 20000,
  transports: ['websocket', 'polling'],
  
  // Auth with reconnection
  auth: (cb) => {
    cb({
      token: getLatestToken(),
    });
  },
});

// Reconnection event handlers
socket.on('connect', () => {
  console.log('Connected with ID:', socket.id);
  // Re-subscribe to rooms
  socket.emit('subscribe', { room: 'updates' });
});

socket.on('disconnect', (reason) => {
  console.log('Disconnected:', reason);
  if (reason === 'io server disconnect') {
    // Server initiated disconnect, do not reconnect
    socket.connect();
  }
});

socket.on('reconnect_attempt', (attemptNumber) => {
  console.log(`Reconnection attempt ${attemptNumber}`);
});

socket.on('reconnect_error', (error) => {
  console.error('Reconnection error:', error.message);
});

socket.on('reconnect_failed', () => {
  console.error('All reconnection attempts failed');
  showOfflineBanner();
});

// Track connection state
socket.on('reconnect', () => {
  console.log('Successfully reconnected');
  restoreApplicationState();
});

Expected output: Socket.IO handles reconnection automatically with configurable backoff, auth token refresh, and state restoration.

Common Mistakes

Mistake Explanation
Using fixed reconnection intervals Fixed intervals cause thundering herds when servers restart; use exponential backoff with jitter
Not limiting reconnection attempts Infinite reconnection attempts without limit can drain battery and bandwidth
Forgetting to handle auth token expiry Tokens may expire during long disconnections; refresh auth on reconnect
Not queuing messages during disconnection Messages sent while disconnected are lost unless queued
Ignoring connection state in UI Users should see connection status indicators so they know when offline

Practice Questions

  1. What is exponential backoff and why is it used for reconnection?
  2. How does jitter prevent thundering herd problems?
  3. How do you handle message delivery guarantees during reconnection?
  4. What is the difference between clean and unclean disconnections?
  5. How does Socket.IO handle reconnection differently from raw WebSocket?

Challenge

Build a WebSocket client with a state machine that tracks connection states (connecting, connected, disconnected, reconnecting, waiting). Implement exponential backoff with jitter, message queuing, and a UI indicator showing connection status. Include auth token refresh on reconnection.

FAQ

Should I reconnect on all disconnections?

No, do not reconnect if the server explicitly closes the connection or the user logs out. Only reconnect on unexpected disconnections.

How many reconnection attempts should I make?

10-20 attempts is reasonable. Beyond that, the network issue is likely persistent and should be surfaced to the user.

How do I prevent duplicate message processing during reconnection?

Use message IDs and deduplication on the server side. Clients can include a last received message ID when reconnecting.

What is the best initial reconnection delay?

Start with 500ms-1s for local networks, 2-5s for internet connections. Adjust based on your application's latency requirements.

How do I handle reconnection in mobile apps?

Mobile apps should use longer delays (5-30s) and respect battery-saving modes. Monitor network state changes to trigger immediate reconnection.

Should I show a reconnection UI?

Yes, show a non-intrusive indicator when offline and a brief reconnection animation. Do not block the entire UI during reconnection.

Mini Project

Build a WebSocket client SDK with advanced reconnection features: adaptive backoff that adjusts based on network conditions, message replay with deduplication, session restoration, and a connection status widget. Include integration with a real chat application.

What's Next

Learn about WebSocket heartbeats and keep-alive

Built by the developers of DodaTech

Doda Browser, DodaZIP & Durga Antivirus Pro