Introduction

WebSocket is a communication protocol that provides full-duplex communication channels over a single TCP connection. It enables real-time, bidirectional communication between clients and servers, making it essential for chat applications, live updates, gaming, and other real-time systems.

This guide covers:

  • WebSocket Fundamentals: Protocol, handshake, and connection lifecycle
  • Connection Management: Establishing, maintaining, and closing connections
  • Message Handling: Text and binary messages, framing
  • Scaling: Horizontal scaling strategies and challenges
  • Best Practices: Error handling, reconnection, and performance

What is WebSocket?

WebSocket is a protocol that:

  • Full-Duplex: Bidirectional communication
  • Low Latency: Real-time data transfer
  • Persistent Connection: Single TCP connection
  • Efficient: Minimal overhead after handshake
  • Web Standard: Works in browsers and servers

Key Concepts

Handshake: Initial HTTP upgrade request

Frame: Unit of data transmission

Connection: Persistent TCP connection

Message: Application-level data unit

Close: Graceful connection termination

Architecture

High-Level Architecture

┌─────────────┐     ┌─────────────┐     ┌─────────────┐
│   Client    │────▶│   Client    │────▶│   Client    │
│  (Browser)  │     │  (Mobile)   │     │  (Desktop)  │
└──────┬──────┘     └──────┬──────┘     └──────┬──────┘
       │                    │                    │
       └────────────────────┴────────────────────┘
                            │
                            │ WebSocket Connection
                            │
                            ▼
              ┌─────────────────────────┐
              │   Load Balancer        │
              │   / API Gateway        │
              └──────┬──────────────────┘
                     │
                     ▼
              ┌─────────────────────────┐
              │   WebSocket Server      │
              │                         │
              │  ┌──────────┐           │
              │  │Connection│           │
              │  │ Manager  │           │
              │  └────┬─────┘           │
              │       │                 │
              │  ┌────┴─────┐           │
              │  │ Message  │           │
              │  │ Handler  │           │
              │  └──────────┘           │
              └──────┬──────────────────┘
                     │
       ┌─────────────┴─────────────┐
       │                           │
┌──────▼──────┐           ┌───────▼──────┐
│  Backend    │           │  Pub/Sub     │
│  Services   │           │  System      │
└─────────────┘           └─────────────┘

Explanation:

  • Clients: Web browsers, mobile apps, or desktop applications that establish WebSocket connections for real-time bidirectional communication.
  • Load Balancer / API Gateway: Routes WebSocket connections and handles SSL termination.
  • WebSocket Server: Maintains persistent connections with clients and handles message routing.
  • Connection Manager: Manages WebSocket connections, handles connection lifecycle, and maintains connection pools.
  • Message Handler: Processes incoming messages, routes them to appropriate handlers, and manages message framing.
  • Backend Services: Business logic services and Pub/Sub systems that process messages and broadcast updates.

Core Architecture

┌─────────────────────────────────────────────────────────┐
│              WebSocket Clients                          │
│  (Browsers, Mobile Apps, Desktop Apps)                  │
└────────────────────┬────────────────────────────────────┘
                     │
                     │ HTTP Upgrade Request
                     │ (WebSocket Handshake)
                     │
┌────────────────────▼────────────────────────────────────┐
│              Load Balancer / API Gateway                  │
│  (SSL Termination, Routing)                               │
└────────────────────┬────────────────────────────────────┘
                     │
                     │ WebSocket Connection
                     │
┌────────────────────▼────────────────────────────────────┐
│              WebSocket Server                            │
│                                                           │
│  ┌──────────────────────────────────────────────────┐    │
│  │         Connection Manager                        │    │
│  │  (Connection Pool, Lifecycle Management)         │    │
│  └──────────────────────────────────────────────────┘    │
│                          │                                │
│  ┌──────────────────────────────────────────────────┐    │
│  │         Message Handler                          │    │
│  │  (Frame Parsing, Message Routing)              │    │
│  └──────────────────────────────────────────────────┘    │
│                          │                                │
│        ┌─────────────────┴─────────────────┐            │
│        │                                     │            │
│  ┌─────▼──────┐                    ┌───────▼──────┐     │
│  │  Business   │                    │   Pub/Sub     │     │
│  │   Logic     │                    │   System      │     │
│  └────────────┘                    └──────────────┘     │
│        │                                     │            │
│        └─────────────────┬─────────────────┘            │
│                          │                                │
│  ┌───────────────────────▼──────────────────────────┐   │
│  │         Backend Services                           │   │
│  │  (Database, Cache, Message Queue)                  │   │
│  └──────────────────────────────────────────────────┘   │
└───────────────────────────────────────────────────────────┘

WebSocket Protocol

Handshake

Client Request:

GET /chat HTTP/1.1
Host: server.example.com
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==
Sec-WebSocket-Version: 13

Server Response:

HTTP/1.1 101 Switching Protocols
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=

Connection Lifecycle

1. HTTP Handshake
   ↓
2. Connection Established
   ↓
3. Data Exchange (Bidirectional)
   ↓
4. Close Handshake
   ↓
5. Connection Closed

Server Implementation

Node.js (ws library)

const WebSocket = require('ws');

const wss = new WebSocket.Server({ port: 8080 });

wss.on('connection', function connection(ws, req) {
  console.log('Client connected');
  
  // Send message to client
  ws.send('Welcome to WebSocket server');
  
  // Receive message from client
  ws.on('message', function incoming(message) {
    console.log('Received: %s', message);
    
    // Echo message back
    ws.send(`Echo: ${message}`);
  });
  
  // Handle connection close
  ws.on('close', function close() {
    console.log('Client disconnected');
  });
  
  // Handle errors
  ws.on('error', function error(err) {
    console.error('WebSocket error:', err);
  });
});

Python (websockets library)

import asyncio
import websockets

async def handle_client(websocket, path):
    print('Client connected')
    
    # Send welcome message
    await websocket.send('Welcome to WebSocket server')
    
    try:
        async for message in websocket:
            print(f'Received: {message}')
            # Echo message back
            await websocket.send(f'Echo: {message}')
    except websockets.exceptions.ConnectionClosed:
        print('Client disconnected')

async def main():
    async with websockets.serve(handle_client, "localhost", 8080):
        await asyncio.Future()  # run forever

asyncio.run(main())

Go (gorilla/websocket)

package main

import (
    "log"
    "net/http"
    "github.com/gorilla/websocket"
)

var upgrader = websocket.Upgrader{
    CheckOrigin: func(r *http.Request) bool {
        return true
    },
}

func handleWebSocket(w http.ResponseWriter, r *http.Request) {
    conn, err := upgrader.Upgrade(w, r, nil)
    if err != nil {
        log.Println("Upgrade error:", err)
        return
    }
    defer conn.Close()
    
    log.Println("Client connected")
    
    // Send welcome message
    conn.WriteMessage(websocket.TextMessage, []byte("Welcome"))
    
    for {
        // Read message
        messageType, message, err := conn.ReadMessage()
        if err != nil {
            log.Println("Read error:", err)
            break
        }
        
        log.Printf("Received: %s", message)
        
        // Echo message back
        err = conn.WriteMessage(messageType, message)
        if err != nil {
            log.Println("Write error:", err)
            break
        }
    }
}

func main() {
    http.HandleFunc("/ws", handleWebSocket)
    log.Fatal(http.ListenAndServe(":8080", nil))
}

Client Implementation

JavaScript (Browser)

// Create WebSocket connection
const ws = new WebSocket('ws://localhost:8080');

// Connection opened
ws.onopen = function(event) {
    console.log('Connected to server');
    ws.send('Hello Server!');
};

// Receive message
ws.onmessage = function(event) {
    console.log('Received:', event.data);
};

// Connection closed
ws.onclose = function(event) {
    console.log('Connection closed');
};

// Error handling
ws.onerror = function(error) {
    console.error('WebSocket error:', error);
};

// Send message
function sendMessage(message) {
    if (ws.readyState === WebSocket.OPEN) {
        ws.send(message);
    }
}

Reconnection Logic

class WebSocketClient {
    constructor(url) {
        this.url = url;
        this.ws = null;
        this.reconnectInterval = 1000;
        this.maxReconnectInterval = 30000;
        this.reconnectDecay = 1.5;
        this.shouldReconnect = true;
    }
    
    connect() {
        this.ws = new WebSocket(this.url);
        
        this.ws.onopen = () => {
            console.log('Connected');
            this.reconnectInterval = 1000;
        };
        
        this.ws.onclose = () => {
            if (this.shouldReconnect) {
                this.reconnect();
            }
        };
        
        this.ws.onerror = (error) => {
            console.error('WebSocket error:', error);
        };
    }
    
    reconnect() {
        setTimeout(() => {
            console.log('Reconnecting...');
            this.connect();
            this.reconnectInterval = Math.min(
                this.reconnectInterval * this.reconnectDecay,
                this.maxReconnectInterval
            );
        }, this.reconnectInterval);
    }
    
    send(message) {
        if (this.ws && this.ws.readyState === WebSocket.OPEN) {
            this.ws.send(message);
        }
    }
    
    close() {
        this.shouldReconnect = false;
        if (this.ws) {
            this.ws.close();
        }
    }
}

Message Types

Text Messages

// Send text
ws.send('Hello, Server!');

// Receive text
ws.onmessage = function(event) {
    const message = event.data; // String
    console.log('Text message:', message);
};

Binary Messages

// Send binary
const buffer = new ArrayBuffer(8);
const view = new Uint8Array(buffer);
ws.send(buffer);

// Receive binary
ws.onmessage = function(event) {
    if (event.data instanceof ArrayBuffer) {
        const view = new Uint8Array(event.data);
        console.log('Binary message:', view);
    }
};

JSON Messages

// Send JSON
const message = { type: 'chat', text: 'Hello' };
ws.send(JSON.stringify(message));

// Receive JSON
ws.onmessage = function(event) {
    const message = JSON.parse(event.data);
    console.log('JSON message:', message);
};

Scaling Strategies

1. Sticky Sessions (Session Affinity)

Load Balancer Configuration:

Client → Load Balancer (Sticky Session) → WebSocket Server

Pros:

  • Simple to implement
  • Maintains connection

Cons:

  • Uneven load distribution
  • Server failure loses connections

2. Message Broker (Pub/Sub)

Architecture:

Client → WebSocket Server → Message Broker (Redis/Kafka) → Other Servers

Implementation:

const redis = require('redis');
const subscriber = redis.createClient();
const publisher = redis.createClient();

// Subscribe to channel
subscriber.subscribe('messages');

// Broadcast message
wss.on('connection', function connection(ws) {
    ws.on('message', function incoming(message) {
        // Publish to all servers
        publisher.publish('messages', message);
    });
    
    // Receive from other servers
    subscriber.on('message', function(channel, message) {
        ws.send(message);
    });
});

Benefits:

  • Horizontal scaling
  • Message distribution
  • Decoupled servers

3. Shared State (Redis)

Store Connection Info:

const redis = require('redis');
const client = redis.createClient();

// Store connection
wss.on('connection', function connection(ws, req) {
    const userId = getUserId(req);
    
    // Store connection mapping
    client.set(`ws:${userId}`, ws.id);
    
    ws.on('close', function() {
        client.del(`ws:${userId}`);
    });
});

// Send to specific user
async function sendToUser(userId, message) {
    const serverId = await client.get(`ws:${userId}`);
    if (serverId === currentServerId) {
        // Send directly
    } else {
        // Forward to other server
        forwardMessage(serverId, userId, message);
    }
}

Performance Optimization

Connection Pooling

Reuse Connections:

class ConnectionPool {
    constructor(maxConnections = 100) {
        this.connections = new Map();
        this.maxConnections = maxConnections;
    }
    
    add(userId, ws) {
        if (this.connections.size >= this.maxConnections) {
            // Remove oldest connection
            const firstKey = this.connections.keys().next().value;
            this.connections.get(firstKey).close();
            this.connections.delete(firstKey);
        }
        this.connections.set(userId, ws);
    }
    
    get(userId) {
        return this.connections.get(userId);
    }
    
    remove(userId) {
        this.connections.delete(userId);
    }
}

Heartbeat/Ping-Pong

Keep Connection Alive:

// Server
setInterval(function ping() {
    wss.clients.forEach(function each(ws) {
        if (ws.isAlive === false) {
            return ws.terminate();
        }
        ws.isAlive = false;
        ws.ping();
    });
}, 30000);

ws.on('pong', function() {
    this.isAlive = true;
});

// Client
ws.on('pong', function() {
    console.log('Pong received');
});

setInterval(function() {
    if (ws.readyState === WebSocket.OPEN) {
        ws.ping();
    }
}, 30000);

Message Batching

Batch Messages:

class MessageBatcher {
    constructor(batchSize = 10, batchInterval = 100) {
        this.batch = [];
        this.batchSize = batchSize;
        this.batchInterval = batchInterval;
    }
    
    add(message) {
        this.batch.push(message);
        
        if (this.batch.length >= this.batchSize) {
            this.flush();
        } else {
            setTimeout(() => this.flush(), this.batchInterval);
        }
    }
    
    flush() {
        if (this.batch.length > 0) {
            ws.send(JSON.stringify(this.batch));
            this.batch = [];
        }
    }
}

Security

Authentication

Token-Based Auth:

const jwt = require('jsonwebtoken');

wss.on('connection', function connection(ws, req) {
    const token = new URL(req.url, 'http://localhost').searchParams.get('token');
    
    try {
        const decoded = jwt.verify(token, SECRET);
        ws.userId = decoded.userId;
    } catch (err) {
        ws.close(1008, 'Invalid token');
        return;
    }
    
    // Handle connection
});

Rate Limiting

Limit Messages:

const rateLimit = new Map();

wss.on('connection', function connection(ws) {
    const clientId = ws._socket.remoteAddress;
    rateLimit.set(clientId, { count: 0, resetTime: Date.now() + 60000 });
    
    ws.on('message', function incoming(message) {
        const limit = rateLimit.get(clientId);
        
        if (Date.now() > limit.resetTime) {
            limit.count = 0;
            limit.resetTime = Date.now() + 60000;
        }
        
        if (limit.count >= 100) {
            ws.close(1008, 'Rate limit exceeded');
            return;
        }
        
        limit.count++;
        // Process message
    });
});

Best Practices

1. Connection Management

  • Implement reconnection logic
  • Use heartbeat/ping-pong
  • Handle connection errors gracefully
  • Clean up on disconnect

2. Message Handling

  • Validate message format
  • Handle binary and text messages
  • Implement message queuing
  • Batch messages when possible

3. Scaling

  • Use message broker for multi-server
  • Implement sticky sessions if needed
  • Monitor connection count
  • Plan for horizontal scaling

4. Security

  • Authenticate connections
  • Implement rate limiting
  • Use WSS (WebSocket Secure)
  • Validate input

What Interviewers Look For

Real-Time Communication Understanding

  1. WebSocket Concepts
    • Understanding of protocol, handshake
    • Connection lifecycle
    • Message handling
    • Red Flags: No WebSocket understanding, wrong protocol, poor connection handling
  2. Scaling Challenges
    • Horizontal scaling strategies
    • Message distribution
    • State management
    • Red Flags: No scaling plan, single server, poor distribution
  3. Performance
    • Connection management
    • Message batching
    • Heartbeat mechanism
    • Red Flags: No optimization, poor performance, no heartbeat

Problem-Solving Approach

  1. Connection Management
    • Reconnection logic
    • Error handling
    • Connection pooling
    • Red Flags: No reconnection, poor error handling, no pooling
  2. Scaling Design
    • Multi-server architecture
    • Message distribution
    • State synchronization
    • Red Flags: No scaling, poor architecture, no synchronization

System Design Skills

  1. Real-Time Architecture
    • WebSocket server design
    • Message broker integration
    • Load balancing
    • Red Flags: No architecture, poor design, no load balancing
  2. Scalability
    • Horizontal scaling
    • Message distribution
    • Performance optimization
    • Red Flags: No scaling, poor distribution, no optimization

Communication Skills

  1. Clear Explanation
    • Explains WebSocket concepts
    • Discusses trade-offs
    • Justifies design decisions
    • Red Flags: Unclear explanations, no justification, confusing

Meta-Specific Focus

  1. Real-Time Systems Expertise
    • Understanding of real-time communication
    • WebSocket mastery
    • Scaling patterns
    • Key: Demonstrate real-time systems expertise
  2. System Design Skills
    • Can design real-time systems
    • Understands scaling challenges
    • Makes informed trade-offs
    • Key: Show practical real-time design skills

Summary

WebSocket Key Points:

  • Full-Duplex: Bidirectional real-time communication
  • Low Latency: Minimal overhead after handshake
  • Persistent Connection: Single TCP connection
  • Scaling: Message broker, sticky sessions, shared state
  • Performance: Connection pooling, heartbeat, batching

Common Use Cases:

  • Chat applications
  • Live notifications
  • Real-time gaming
  • Collaborative editing
  • Live dashboards
  • Trading platforms

Best Practices:

  • Implement reconnection logic
  • Use heartbeat/ping-pong
  • Handle errors gracefully
  • Scale with message broker
  • Authenticate connections
  • Implement rate limiting
  • Batch messages when possible

WebSocket is essential for building real-time applications that require low-latency, bidirectional communication.