SalakCode SalakCode
Networking

WebSockets

Real-time bidirectional communication

Intermediate
networking real-time websocket

Definition

WebSockets provide a persistent, low-latency, full-duplex connection between client and server. Unlike HTTP polling, WebSockets maintain an open TCP connection, enabling real-time bidirectional data flow perfect for chat apps, live updates, and gaming.

Basic Usage

Creating a Connection

const socket = new WebSocket('wss://example.com/socket');

// Connection opened
socket.addEventListener('open', (event) => {
  console.log('Connected to server');
  socket.send('Hello Server!');
});

// Listen for messages
socket.addEventListener('message', (event) => {
  console.log('Message from server:', event.data);
});

// Connection closed
socket.addEventListener('close', (event) => {
  console.log('Disconnected from server');
});

// Handle errors
socket.addEventListener('error', (error) => {
  console.error('WebSocket error:', error);
});

Connection States

console.log(socket.readyState);
// 0 - CONNECTING
// 1 - OPEN
// 2 - CLOSING
// 3 - CLOSED

// Check if open before sending
if (socket.readyState === WebSocket.OPEN) {
  socket.send(data);
}

Sending Data

Text Messages

socket.send('Hello World');

Binary Data

// Send ArrayBuffer
const buffer = new ArrayBuffer(128);
socket.send(buffer);

// Send Blob
const blob = new Blob(['Hello'], { type: 'text/plain' });
socket.send(blob);

JSON Messages

const message = {
  type: 'chat',
  user: 'John',
  text: 'Hello everyone!',
  timestamp: Date.now()
};

socket.send(JSON.stringify(message));

// Receiving JSON
socket.addEventListener('message', (event) => {
  const data = JSON.parse(event.data);
  console.log(data.user, 'says:', data.text);
});

Advanced Patterns

Reconnection Logic

class ReconnectingWebSocket {
  constructor(url, options = {}) {
    this.url = url;
    this.reconnectInterval = options.reconnectInterval || 1000;
    this.maxReconnects = options.maxReconnects || 5;
    this.reconnectCount = 0;
    this.listeners = {};
    
    this.connect();
  }
  
  connect() {
    this.socket = new WebSocket(this.url);
    
    this.socket.onopen = () => {
      console.log('Connected');
      this.reconnectCount = 0;
      this.emit('open');
    };
    
    this.socket.onmessage = (event) => {
      this.emit('message', event);
    };
    
    this.socket.onclose = () => {
      this.emit('close');
      this.attemptReconnect();
    };
    
    this.socket.onerror = (error) => {
      this.emit('error', error);
    };
  }
  
  attemptReconnect() {
    if (this.reconnectCount < this.maxReconnects) {
      this.reconnectCount++;
      console.log(`Reconnecting... (${this.reconnectCount}/${this.maxReconnects})`);
      
      setTimeout(() => this.connect(), this.reconnectInterval);
    }
  }
  
  send(data) {
    if (this.socket.readyState === WebSocket.OPEN) {
      this.socket.send(data);
    }
  }
  
  on(event, callback) {
    if (!this.listeners[event]) {
      this.listeners[event] = [];
    }
    this.listeners[event].push(callback);
  }
  
  emit(event, data) {
    if (this.listeners[event]) {
      this.listeners[event].forEach(callback => callback(data));
    }
  }
  
  close() {
    this.socket.close();
  }
}

// Usage
const ws = new ReconnectingWebSocket('wss://chat.example.com');
ws.on('message', (event) => console.log(event.data));

Heartbeat/Ping

class WebSocketClient {
  constructor(url) {
    this.url = url;
    this.socket = null;
    this.pingInterval = null;
    this.missedPongs = 0;
  }
  
  connect() {
    this.socket = new WebSocket(this.url);
    
    this.socket.onopen = () => {
      this.startHeartbeat();
    };
    
    this.socket.onmessage = (event) => {
      if (event.data === 'pong') {
        this.missedPongs = 0;
      } else {
        this.handleMessage(event.data);
      }
    };
    
    this.socket.onclose = () => {
      this.stopHeartbeat();
    };
  }
  
  startHeartbeat() {
    this.pingInterval = setInterval(() => {
      if (this.missedPongs > 2) {
        this.socket.close();
        return;
      }
      
      this.socket.send('ping');
      this.missedPongs++;
    }, 30000); // 30 seconds
  }
  
  stopHeartbeat() {
    clearInterval(this.pingInterval);
  }
}

Use Cases

Real-time Chat

const chatSocket = new WebSocket('wss://chat.example.com');

// Send message
function sendMessage(text) {
  chatSocket.send(JSON.stringify({
    type: 'message',
    text: text,
    timestamp: Date.now()
  }));
}

// Receive messages
chatSocket.onmessage = (event) => {
  const message = JSON.parse(event.data);
  
  switch(message.type) {
    case 'message':
      displayMessage(message);
      break;
    case 'user_joined':
      showNotification(`${message.user} joined`);
      break;
    case 'user_left':
      showNotification(`${message.user} left`);
      break;
  }
};

Live Dashboard

const dashboardSocket = new WebSocket('wss://metrics.example.com');

dashboardSocket.onmessage = (event) => {
  const update = JSON.parse(event.data);
  
  updateChart(update.metric, update.value);
  updateCounter(update.metric, update.value);
};

Security Considerations

// Always use WSS (WebSocket Secure) in production
const socket = new WebSocket('wss://secure.example.com');

// Validate origin on server
// Implement authentication
// Use subprotocols for versioning
const socket = new WebSocket('wss://example.com', ['chat-v1']);
Key Takeaway

WebSockets enable real-time bidirectional communication ideal for chat, live updates, and gaming. Implement reconnection logic for reliability, use heartbeats to detect dead connections, and always use WSS in production. Remember that WebSockets maintain persistent connections, so implement proper connection management.

Resources

Related Topics