Real-Time Communication: Event Streams vs. WebSockets (With Mermaid Diagrams & Express.js)
Modern applications like chat apps, live dashboards, and multiplayer games require real-time data transfer. Two key technologies enable this:
- Event Streams (SSE - Server-Sent Events) – Unidirectional (server → client) over HTTP.
- WebSockets – Full-duplex (client ↔ server) with persistent connections.
This guide explains both with Mermaid diagrams, Express.js examples, performance metrics, and comparisons.
1. Event Streams (SSE) – Simple Real-Time Updates
How SSE Works
SSE uses a long-lived HTTP connection where the server pushes updates to the client.
Key Features
✅ Built-in reconnection (auto-retry if connection drops).
✅ Text-based (UTF-8, no binary support).
✅ Works over HTTP/1.1 and HTTP/2.
❌ Unidirectional (server → client only).
Express.js SSE Example
Server-Side (Node.js + Express)
const express = require('express');
const app = express();
app.get('/sse', (req, res) => {
res.setHeader('Content-Type', 'text/event-stream');
res.setHeader('Cache-Control', 'no-cache');
res.setHeader('Connection', 'keep-alive');
// Send data every second
const interval = setInterval(() => {
res.write(`data: ${JSON.stringify({ time: new Date().toISOString() })}\n\n`);
}, 1000);
req.on('close', () => clearInterval(interval));
});
app.listen(3000, () => console.log('SSE running on http://localhost:3000/sse'));
Client-Side (Browser JavaScript)
const eventSource = new EventSource('http://localhost:3000/sse');
eventSource.onmessage = (event) => {
const data = JSON.parse(event.data);
console.log('Update:', data.time);
};
eventSource.onerror = () => {
console.log('Connection lost. Reconnecting...');
};
2. WebSockets – Full-Duplex Real-Time Communication
How WebSockets Work
- Handshake: Client sends an HTTP
Upgrade: websocket
request. - Persistent Connection: After handshake, data flows both ways with minimal overhead.
Key Features
✅ Bidirectional (client ↔ server).
✅ Low latency (~1-50ms).
✅ Supports binary & text data.
❌ No auto-reconnect (must implement manually).
Express.js WebSocket Example (ws
Library)
Server-Side (Express + WebSocket)
const express = require('express');
const WebSocket = require('ws');
const app = express();
const server = app.listen(3000);
const wss = new WebSocket.Server({ server });
wss.on('connection', (ws) => {
console.log('New client connected');
ws.on('message', (message) => {
console.log('Received:', message.toString());
ws.send(`Echo: ${message}`);
});
ws.on('close', () => {
console.log('Client disconnected');
});
});
Client-Side (Browser JavaScript)
const socket = new WebSocket('ws://localhost:3000');
socket.onopen = () => {
console.log('Connected!');
socket.send('Hello Server!');
};
socket.onmessage = (event) => {
console.log('Server says:', event.data);
};
socket.onclose = () => {
console.log('Disconnected. Reconnecting...');
setTimeout(() => location.reload(), 1000); // Simple reconnect
}
3. SSE vs. WebSockets – Comparison Table
Feature | SSE (Event Streams) | WebSockets |
---|---|---|
Direction | Server → Client only | Bidirectional (Client ↔ Server) |
Protocol | HTTP (text/event-stream) | WebSocket (after HTTP upgrade) |
Reconnection | Automatic | Manual implementation needed |
Data Format | Text (UTF-8) only | Text + Binary |
Latency | ~100-300ms (HTTP overhead) | ~1-50ms (low overhead) |
Browser Support | All except legacy IE/Edge | All modern browsers |
Best For | Live feeds, notifications | Chat, gaming, real-time apps |
4. When to Use Which?
Use SSE If:
✔ You only need server-to-client updates (e.g., stock prices, live tweets).
✔ You want simplicity (no extra libraries, just HTTP).
✔ You need auto-reconnect handling.
Use WebSockets If:
✔ You need two-way communication (e.g., chat, multiplayer games).
✔ You require lowest possible latency.
✔ You need binary data support (e.g., video streaming).
5. Advanced: Scaling & Performance Tips
For SSE:
- Use HTTP/2 for better multiplexing.
- Avoid blocking the main thread (use worker threads for heavy processing).
For WebSockets:
- Enable
permessage-deflate
for compression. - Use Redis Pub/Sub for horizontal scaling.
6. Express.js Pro Tip: Use socket.io
for Fallback Support
socket.io
provides:
✅ WebSocket + HTTP long-polling fallback (works even behind proxies).
✅ Rooms & namespaces for complex apps.
const express = require('express');
const http = require('http');
const { Server } = require('socket.io');
const app = express();
const server = http.createServer(app);
const io = new Server(server);
io.on('connection', (socket) => {
socket.emit('welcome', 'Connected to socket.io!');
socket.on('chat', (msg) => {
io.emit('message', msg); // Broadcast to all
});
});
server.listen(3000);
7. References & Further Reading
📌 MDN: Server-Sent Events
📌 WebSocket API Docs
📌 ws
Library (Lightweight WebSockets)
📌 Socket.io (Production-Grade WebSockets)
Final Verdict
- SSE = Best for simple, server-pushed updates.
- WebSockets = Best for interactive, low-latency apps.
Need help scaling? Let me know—I’ll cover load balancing & Redis integration next! 🚀