Back to All Concepts
NetworkingReal-timeAPI DesignIntermediate

WebSockets vs Polling

Comprehensive comparison of real-time communication techniques including short polling, long polling, Server-Sent Events (SSE), and WebSockets for building responsive web applications.

Real-Time Communication Patterns

How does a chat app instantly show new messages? How do stock tickers update in real-time? Standard HTTP is request-response: the server can't "push" data to clients. This article explores four techniques for achieving real-time updates.

The Problem: Server Can't Initiate

Traditional HTTP is client-initiated:

Client: "GET /messages" → Server: "Here are 10 messages"
Client: "GET /messages" → Server: "Here are 10 messages (same ones)"
Click to expand code...

Problem: Server has new data but can't notify client!


Pattern 1: Short Polling

Client repeatedly asks server for updates at regular intervals.

mermaid
sequenceDiagram
    participant Client
    participant Server
    
    Client->>Server: GET /messages (request #1)
    Server-->>Client: [] (no new messages)
    Note over Client: Wait 5 seconds
    Client->>Server: GET /messages (request #2)
    Server-->>Client: [] (no new messages)
    Note over Client: Wait 5 seconds
    Client->>Server: GET /messages (request #3)
    Server-->>Client: ["New message!"]
Click to expand code...

Implementation

javascript
// Client-side (JavaScript)
function shortPolling() {
    setInterval(async () => {
        const response = await fetch('/api/messages');
        const messages = await response.json();
        
        if (messages.length > 0) {
            updateUI(messages);
        }
    }, 5000); // Poll every 5 seconds
}

shortPolling();
Click to expand code...
python
# Server-side (Flask)
@app.route('/api/messages')
def get_messages():
    messages = db.get_new_messages(user_id)
    return jsonify(messages)
Click to expand code...

Pros & Cons

Pros:

  • Extremely simple to implement
  • Works with any HTTP server
  • No special protocols needed

Cons:

  • Wasteful: 99% of requests return empty if updates are rare
  • High latency: Up to polling interval (5s delay)
  • Server load: N clients × polling frequency
  • Bandwidth waste: HTTP headers on every request

Best For

  • Very simple apps with few users
  • Updates can tolerate 5-10 second delays

Pattern 2: Long Polling (Comet)

Client sends request, server holds it open until new data arrives.

mermaid
sequenceDiagram
    participant Client
    participant Server
    
    Client->>Server: GET /messages (request held open)
    Note over Server: Wait... wait... new message arrives!
    Server-->>Client: ["New message!"] (30s later)
    Client->>Server: GET /messages (immediately reconnect)
    Note over Server: Wait... wait...
Click to expand code...

Implementation

javascript
// Client-side
async function longPolling() {
    while (true) {
        try {
            const response = await fetch('/api/messages/poll', {
                // Long timeout
                signal: AbortSignal.timeout(60000)
            });
            
            const messages = await response.json();
            updateUI(messages);
            
            // Immediately reconnect
        } catch (error) {
            // Timeout or error, reconnect after brief delay
            await sleep(1000);
        }
    }
}

longPolling();
Click to expand code...
python
# Server-side (with timeout)
@app.route('/api/messages/poll')
def poll_messages():
    timeout = 30  # Hold connection for max 30s
    start_time = time.time()
    
    while time.time() - start_time < timeout:
        messages = db.get_new_messages(user_id)
        if messages:
            return jsonify(messages)
        time.sleep(0.5)  # Check every 500ms
    
    return jsonify([])  # Timeout, return empty
Click to expand code...

Pros & Cons

Pros:

  • Lower latency than short polling (near-instant)
  • Fewer requests than short polling
  • Works with standard HTTP

Cons:

  • Still creates new HTTP connection for each response
  • HTTP header overhead on every reconnect
  • Connection timeout management complex
  • Scales poorly (holds server resources per client)

Best For

  • Applications needing lower latency than short polling
  • When WebSocket infrastructure not available

Pattern 3: Server-Sent Events (SSE)

One-way channel from server to client over HTTP.

mermaid
sequenceDiagram
    participant Client
    participant Server
    
    Client->>Server: GET /events (opens stream)
    Server-->>Client: data: message 1
    Server-->>Client: data: message 2
    Server-->>Client: data: message 3
    Note over Client,Server: Connection stays open
Click to expand code...

Implementation

javascript
// Client-side
const evtSource = new EventSource('/api/events');

evtSource.onmessage = (event) => {
    const data = JSON.parse(event.data);
    updateUI(data);
};

evtSource.onerror = () => {
    console.log('Connection lost, reconnecting...');
};
Click to expand code...
python
# Server-side (Flask)
@app.route('/api/events')
def stream():
    def generate():
        while True:
            # Check for new data
            messages = db.get_new_messages(user_id)
            if messages:
                for msg in messages:
                    yield f"data: {json.dumps(msg)}\n\n"
            time.sleep(1)
    
    return Response(generate(), mimetype='text/event-stream')
Click to expand code...
python
# Server-side (FastAPI)
from fastapi.responses import StreamingResponse

@app.get("/events")
async def events():
    async def event_generator():
        while True:
            messages = await get_new_messages(user_id)
            if messages:
                for msg in messages:
                    yield f"data: {json.dumps(msg)}\n\n"
            await asyncio.sleep(1)
    
    return StreamingResponse(event_generator(), media_type="text/event-stream")
Click to expand code...

Pros & Cons

Pros:

  • Built into browsers (EventSource API)
  • Automatic reconnection
  • Efficient (single long-lived connection)
  • Works over HTTP/1.1 and HTTP/2

Cons:

  • One-way only (server → client)
  • Limited browser support for custom headers
  • Connection limit per domain (6 in HTTP/1.1)

Best For

  • Stock tickers, live scores, dashboards
  • Notifications, progress updates
  • Any server-to-client streaming

Pattern 4: WebSockets

Bidirectional, full-duplex communication over single TCP connection.

mermaid
sequenceDiagram
    participant Client
    participant Server
    
    Client->>Server: HTTP Upgrade: websocket
    Server-->>Client: 101 Switching Protocols
    Note over Client,Server: Connection upgraded to WebSocket
    
    Client->>Server: message 1
    Server->>Client: message 2
    Client->>Server: message 3
    Server->>Client: message 4
    Note over Client,Server: Both can send anytime
Click to expand code...

Upgrade Handshake

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

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

# Connection now uses WebSocket protocol
Click to expand code...

Implementation

javascript
// Client-side
const ws = new WebSocket('ws://example.com/chat');

ws.onopen = () => {
    console.log('Connected!');
    ws.send(JSON.stringify({ type: 'join', room: 'general' }));
};

ws.onmessage = (event) => {
    const message = JSON.parse(event.data);
    updateUI(message);
};

ws.onclose = () => {
    console.log('Disconnected, reconnecting...');
    setTimeout(connectWebSocket, 1000);
};

// Send message
function sendMessage(text) {
    ws.send(JSON.stringify({ type: 'message', text }));
}
Click to expand code...
python
# Server-side (Python with websockets library)
import asyncio
import websockets
import json

connected_clients = set()

async def chat_handler(websocket, path):
    connected_clients.add(websocket)
    
    try:
        async for message in websocket:
            data = json.loads(message)
            
            # Broadcast to all connected clients
            await asyncio.gather(
                *[client.send(message) for client in connected_clients]
            )
    finally:
        connected_clients.remove(websocket)

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

asyncio.run(main())
Click to expand code...
javascript
// Server-side (Node.js with ws library)
const WebSocket = require('ws');
const wss = new WebSocket.Server({ port: 8080 });

wss.on('connection', (ws) => {
    console.log('Client connected');
    
    ws.on('message', (message) => {
        const data = JSON.parse(message);
        
        // Broadcast to all clients
        wss.clients.forEach((client) => {
            if (client.readyState === WebSocket.OPEN) {
                client.send(JSON.stringify(data));
            }
        });
    });
    
    ws.on('close', () => {
        console.log('Client disconnected');
    });
});
Click to expand code...

Pros & Cons

Pros:

  • True bidirectional communication
  • Low latency (no HTTP overhead after handshake)
  • Efficient: Single connection, minimal overhead
  • Full-duplex: Both sides send simultaneously

Cons:

  • More complex than HTTP
  • Load balancer/proxy support needed
  • Connection state management required
  • Not RESTful (different mental model)

Best For

  • Chat applications
  • Multiplayer games
  • Collaborative editing (Google Docs)
  • Real-time dashboards
  • Trading platforms

Comparison Table

FeatureShort PollingLong PollingSSEWebSockets
LatencyHigh (5-10s)Low (~1s)Very LowVery Low
Server LoadVery HighHighLowVery Low
BandwidthHighMediumLowVery Low
Bidirectional❌ No❌ No❌ No✅ Yes
ComplexityVery SimpleSimpleSimpleModerate
Browser Support✅ All✅ All✅ Modern✅ Modern
Firewall/Proxy✅ Works✅ Works✅ Works⚠️ May need config
Best ForSimple appsMedium appsNotificationsChat, games

Real-World Examples

Facebook Messenger (WebSockets)

javascript
// Simplified Facebook-style chat
const socket = new WebSocket('wss://chat.facebook.com');

socket.onmessage = (event) => {
    const msg = JSON.parse(event.data);
    
    if (msg.type === 'message') {
        displayMessage(msg);
    } else if (msg.type === 'typing') {
        showTypingIndicator(msg.user);
    } else if (msg.type === 'read') {
        markAsRead(msg.messageId);
    }
};

// Send typing indicator
inputField.addEventListener('input', () => {
    socket.send(JSON.stringify({ type: 'typing', user: currentUser }));
});
Click to expand code...

Stock Market Dashboard (SSE)

python
# Server streams stock prices
@app.route('/stock-stream')
def stock_stream():
    def generate():
        while True:
            prices = get_current_stock_prices(['AAPL', 'GOOGL', 'MSFT'])
            yield f"data: {json.dumps(prices)}\n\n"
            time.sleep(1)  # Update every second
    
    return Response(generate(), mimetype='text/event-stream')
Click to expand code...
javascript
// Client displays live prices
const eventSource = new EventSource('/stock-stream');
eventSource.onmessage = (event) => {
    const prices = JSON.parse(event.data);
    updateStockPrices(prices);
};
Click to expand code...

Slack (WebSockets with Fallback)

Slack uses WebSockets when available, falls back to long polling:

javascript
class SlackConnection {
    connect() {
        if ('WebSocket' in window) {
            this.ws = new WebSocket('wss://slack.com/rtm');
            this.ws.onmessage = this.handleMessage;
        } else {
            // Fallback to long polling
            this.longPoll();
        }
    }
    
    longPoll() {
        fetch('/api/events/poll')
            .then(res => res.json())
            .then(data => {
                this.handleMessage(data);
                this.longPoll(); // Reconnect
            });
    }
}
Click to expand code...

Scaling Considerations

WebSocket Scaling Challenges

100,000 connected WebSocket clients
= 100,000 open TCP connections
= Significant server resources
Click to expand code...

Solutions:

  1. Load Balancing with Sticky Sessions
nginx
upstream websocket_backend {
    ip_hash;  # Same client → same server
    server ws1.example.com;
    server ws2.example.com;
}

server {
    location /ws {
        proxy_pass http://websocket_backend;
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "upgrade";
    }
}
Click to expand code...
  1. Message Broker for Cross-Server Communication
python
# Server 1 receives message, publishes to Redis
await redis.publish('chat:room:general', json.dumps(message))

# All servers subscribe to broadcast channel
async def broadcast_listener():
    pubsub = redis.pubsub()
    await pubsub.subscribe('chat:room:general')
    
    async for message in pubsub.listen():
        # Send to all local WebSocket connections
        await broadcast_to_local_clients(message)
Click to expand code...

Interview Tips 💡

When discussing real-time communication in system design interviews:

  1. Start with requirements: "Do we need bidirectional communication or just server-to-client updates?"
  2. Compare options: "For notifications, SSE works well. For chat, we'd need WebSockets..."
  3. Discuss scaling: "With 1M concurrent connections, we'd need multiple WebSocket servers with Redis pub/sub..."
  4. Mention fallbacks: "Like Slack, we'd implement WebSocket with long polling fallback..."
  5. Consider alternatives: "For simple use cases, short polling might be good enough..."
  6. Real examples: "Facebook Messenger uses WebSockets for instant messaging..."

Related Concepts

  • HTTP/2 and HTTP/3 — Improved protocols for web communication
  • Load Balancing — Distributing WebSocket connections
  • Message Queues — Pub/sub for cross-server WebSocket messaging
  • CDN — Edge caching for WebSocket endpoints
  • WebRTC — Peer-to-peer real-time communication

About ScaleWiki

ScaleWiki is an interactive educational platform dedicated to demystifying distributed systems, software architecture, and system design. Our mission is to provide high-quality, technically accurate resources for software engineers preparing for interviews or solving complex scaling challenges in production.

Read more about our Editorial Guidelines & Authorship.

Educational Disclaimer: The architectural patterns and system designs discussed in this article are based on common industry practices, technical whitepapers, and public engineering blogs. Actual implementations in enterprise environments may vary significantly based on specific product requirements, legacy constraints, and evolving technologies.

Related Articles