All skills
Skillintermediate

Real-Time Communication Alternatives

| Feature | WebSocket | SSE | Long Polling | HTTP/2 Push | WebRTC | |---------|-----------|-----|--------------|-------------|--------| | Bidirectional | Yes | No | Yes | No | Yes | | Real-time | Yes | Yes | Near | Yes | Yes | | Browser Support | Excellent | Good | Universal | Good | Good | | Proxy Issues | Some | Rare | Rare | Some | Some | | Overhead | Low | Low | High | Medium | Medium | | Use

Claude Code Knowledge Pack7/10/2026

Overview

Real-Time Communication Alternatives

Technology Comparison

FeatureWebSocketSSELong PollingHTTP/2 PushWebRTC
BidirectionalYesNoYesNoYes
Real-timeYesYesNearYesYes
Browser SupportExcellentGoodUniversalGoodGood
Proxy IssuesSomeRareRareSomeSome
OverheadLowLowHighMediumMedium
Use CaseChat, gamesFeeds, updatesLegacyAssetsAudio/video

Server-Sent Events (SSE)

When to Use SSE

  • One-way server-to-client communication
  • Live feeds, notifications, stock tickers
  • Automatic reconnection needed
  • Simpler than WebSockets
  • Better firewall/proxy compatibility

SSE Server (Node.js)

const express = require('express');
const app = express();

app.get('/events', (req, res) => {
  // Set SSE headers
  res.setHeader('Content-Type', 'text/event-stream');
  res.setHeader('Cache-Control', 'no-cache');
  res.setHeader('Connection', 'keep-alive');
  res.setHeader('Access-Control-Allow-Origin', '*');

  // Send initial connection message
  res.write('data: {"message": "Connected"}\
\
');

  // Send updates every 5 seconds
  const intervalId = setInterval(() => {
    const data = {
      timestamp: Date.now(),
      value: Math.random()
    };

    res.write(`data: ${JSON.stringify(data)}\
\
`);
  }, 5000);

  // Cleanup on client disconnect
  req.on('close', () => {
    clearInterval(intervalId);
    res.end();
  });
});

app.listen(3000);

SSE Client

const eventSource = new EventSource('http://localhost:3000/events');

eventSource.onmessage = (event) => {
  const data = JSON.parse(event.data);
  console.log('Received:', data);
};

eventSource.onerror = (error) => {
  console.error('SSE error:', error);
  // Automatically reconnects
};

// Named events
eventSource.addEventListener('update', (event) => {
  console.log('Update:', event.data);
});

// Close connection
eventSource.close();

SSE with Express

const express = require('express');
const app = express();

class SSEManager {
  constructor() {
    this.clients = new Set();
  }

  addClient(res) {
    this.clients.add(res);
  }

  removeClient(res) {
    this.clients.delete(res);
  }

  broadcast(event, data) {
    const message = `event: ${event}\
data: ${JSON.stringify(data)}\
\
`;

    this.clients.forEach(client => {
      client.write(message);
    });
  }
}

const sseManager = new SSEManager();

app.get('/events', (req, res) => {
  res.setHeader('Content-Type', 'text/event-stream');
  res.setHeader('Cache-Control', 'no-cache');
  res.setHeader('Connection', 'keep-alive');

  sseManager.addClient(res);

  req.on('close', () => {
    sseManager.removeClient(res);
  });
});

// Broadcast to all clients
setInterval(() => {
  sseManager.broadcast('update', {
    timestamp: Date.now(),
    activeClients: sseManager.clients.size
  });
}, 10000);

app.listen(3000);

Long Polling

When to Use Long Polling

  • Legacy browser support needed
  • Firewall/proxy blocks WebSockets
  • Very infrequent updates
  • Fallback mechanism

Long Polling Server

const express = require('express');
const app = express();

const pendingRequests = new Map();
const messages = [];

app.get('/poll', (req, res) => {
  const clientId = req.query.clientId;

  // If messages available, send immediately
  if (messages.length > 0) {
    res.json({ messages });
    messages.length = 0; // Clear messages
    return;
  }

  // Hold request until timeout or new message
  const timeout = setTimeout(() => {
    pendingRequests.delete(clientId);
    res.json({ messages: [] });
  }, 30000); // 30 second timeout

  pendingRequests.set(clientId, { res, timeout });

  req.on('close', () => {
    clearTimeout(timeout);
    pendingRequests.delete(clientId);
  });
});

app.post('/send', express.json(), (req, res) => {
  messages.push(req.body.message);

  // Respond to all pending requests
  pendingRequests.forEach(({ res, timeout }, clientId) => {
    clearTimeout(timeout);
    res.json({ messages });
    pendingRequests.delete(clientId);
  });

  messages.length = 0; // Clear messages
  res.json({ success: true });
});

app.listen(3000);

Long Polling Client

const clientId = Math.random().toString(36);

async function poll() {
  try {
    const response = await fetch(
      `http://localhost:3000/poll?clientId=${clientId}`,
      { signal: AbortSignal.timeout(35000) }
    );

    const data = await response.json();

    if (data.messages.length > 0) {
      console.log('Received messages:', data.messages);
    }

    // Immediately poll again
    poll();
  } catch (error) {
    console.error('Polling error:', error);
    // Retry after delay
    setTimeout(poll, 5000);
  }
}

poll();

HTTP/2 Server Push (Deprecated)

Note: HTTP/2 Server Push is deprecated and removed from Chrome. Use 103 Early Hints instead.

// Example for historical context only
const http2 = require('http2');
const fs = require('fs');

const server = http2.createSecureServer({
  key: fs.readFileSync('server.key'),
  cert: fs.readFileSync('server.crt')
});

server.on('stream', (stream, headers) => {
  if (headers[':path'] === '/') {
    // Push assets before HTML response
    stream.pushStream({ ':path': '/style.css' }, (err, pushStream) => {
      if (!err) {
        pushStream.respondWithFile('style.css');
      }
    });

    stream.respondWithFile('index.html');
  }
});

server.listen(3000);

Decision Matrix

Choose WebSocket When:

  • Bidirectional communication needed
  • Low latency critical (< 50ms)
  • High message frequency (> 1 msg/sec)
  • Gaming, chat, collaborative editing
  • Binary data transfer
  • Custom protocol needed

Choose SSE When:

  • One-way server-to-client only
  • Stock tickers, live feeds
  • News/notifications
  • Simpler implementation preferred
  • Better proxy compatibility needed
  • Automatic reconnection important

Choose Long Polling When:

  • Legacy browser support required (IE8/9)
  • WebSocket blocked by firewall
  • Very infrequent updates
  • Fallback mechanism only

Choose HTTP Streaming When:

  • Large data transfers
  • File uploads with progress
  • Video/audio streaming
  • One-way data flow

Choose WebRTC When:

  • Peer-to-peer communication
  • Audio/video calls
  • Screen sharing
  • File transfer between peers
  • Low latency P2P needed

Hybrid Approach

// Socket.IO with automatic fallback
const io = require('socket.io')(3000, {
  transports: ['websocket', 'polling'], // Try WebSocket first
  upgrade: true,
  allowUpgrades: true
});

io.on('connection', (socket) => {
  console.log('Connected via:', socket.conn.transport.name);

  socket.conn.on('upgrade', () => {
    console.log('Upgraded to:', socket.conn.transport.name);
  });
});

Performance Characteristics

Latency (p99)

  • WebSocket: 5-20ms
  • SSE: 10-50ms
  • Long Polling: 100-500ms
  • HTTP/2: 20-100ms

Throughput (messages/sec)

  • WebSocket: 10,000+ per connection
  • SSE: 1,000+ per connection
  • Long Polling: 1-10 per connection

Connection Limits (per server)

  • WebSocket: 50,000-100,000
  • SSE: 50,000-100,000
  • Long Polling: 10,000-20,000

Overhead (per message)

  • WebSocket: 2-6 bytes
  • SSE: ~20 bytes
  • Long Polling: 500-2000 bytes (HTTP headers)

Migration Path

From Polling to WebSocket

// Step 1: Support both
app.get('/api/messages', (req, res) => {
  // Legacy polling endpoint
  res.json({ messages: getRecentMessages() });
});

io.on('connection', (socket) => {
  // New WebSocket endpoint
  socket.on('subscribe', (channel) => {
    socket.join(channel);
  });
});

// Step 2: Gradually migrate clients
// Step 3: Deprecate polling endpoint

From SSE to WebSocket

// SSE provides read-only, add WebSocket for writes
app.get('/events', sseHandler);  // Keep for reads

io.on('connection', (socket) => {
  socket.on('action', (data) => {
    // Handle writes via WebSocket
    processAction(data);
  });
});

// Eventually migrate reads to WebSocket too

Best Practices

  1. Start with simplest solution (SSE for one-way)
  2. Use Socket.IO for automatic fallbacks
  3. Monitor actual requirements before over-engineering
  4. Consider mobile/network constraints
  5. Implement graceful degradation
  6. Load test before production
  7. Have fallback strategy
  8. Monitor connection success rates