Skip to main content

πŸ”΄ WebSocket vs SSE vs Long Polling

πŸ“– Definition​

Real-time communication is a technology that enables immediate data exchange between server and client. WebSocket provides bidirectional real-time communication, SSE (Server-Sent Events) pushes data only from server to client, and Long Polling is a real-time communication method using HTTP. Each has its pros and cons, so you need to choose based on usage scenarios.

🎯 Understanding Through Analogies​

Phone Call vs Radio vs Messenger​

Regular HTTP = Mail
You: "Hello?" (Send letter)
↓ (Days later)
Friend: "Hi!" (Reply)
↓ (Days later)
You: "How are you?" (Another letter)

- Slow
- New connection every time
- Not real-time

Long Polling = Phone Call (Waiting)
You: "Let me know if something happens!" (Call and wait)
↓ (Waiting...)
Friend: "I have something to say now!" (Response)
↓ (Call ends)
You: "Let me know again!" (Call again)

- HTTP-based
- Keep connection β†’ Response β†’ Reconnect
- Inefficient

SSE = Radio Broadcast
Friend: "Hello everyone!" (Start broadcast)
"Today's weather is..." (Continuous transmission)
"Next news is..." (Continuous transmission)
You: (Just listening)

- Server β†’ Client (One-way)
- Keep connection
- Simple

WebSocket = Video Call
You: "Hello!" (Instant send)
Friend: "Nice to see you!" (Instant response)
You: "What are you doing?" (Instant send)
Friend: "Coding!" (Instant response)

- Bidirectional real-time
- Keep connection
- Fast and efficient

Restaurant Ordering​

Regular HTTP = Self-Service
1. Go to counter to order food
2. Wait
3. Receive food and return to seat
4. Go to counter again to order drink
5. Wait again

Long Polling = Press Bell and Wait
1. Press bell and wait for staff (Keep connection)
2. Staff comes β†’ "Please order"
3. Order and press bell again
4. Wait again...

SSE = Kitchen Display
Kitchen: "Customer 1, your food is ready!"
Kitchen: "Customer 2, preparing!"
Kitchen: "Customer 3, coming soon!"
You: (Just listening)

WebSocket = Table Service
You: "Water please"
Staff: "Sure, I'll bring it"
You: "Kimchi too"
Staff: "Right away"
Staff: "Here's your food"
You: "Thank you"

- Free conversation
- Quick response

βš™οΈ How It Works​

1. Regular HTTP vs Real-time Communication​

========== Regular HTTP (Request-Response) ==========

Client Server
β”‚ β”‚
β”‚ 1. Connect (Request) β”‚
│────────────────────────>β”‚
β”‚ β”‚
β”‚ β”‚ Processing...
β”‚ β”‚
β”‚ 2. Response β”‚
β”‚<────────────────────────│
β”‚ β”‚
β”‚ Connection closed β”‚
β•³ β•³

β”‚ 3. Connect again β”‚
│────────────────────────>β”‚
β”‚ β”‚

Features:
- Response only when client requests
- New connection every time
- Server cannot send first
- Not real-time

========== Real-time Communication ==========

Client Server
β”‚ β”‚
β”‚ Connection β”‚
β”‚<───────────────────────>β”‚
β”‚ β”‚
β”‚ Bidirectional communication maintained
β”‚<───────────────────────>β”‚
β”‚ β”‚
β”‚ Data exchange β”‚
β”‚<───────────────────────>β”‚
β”‚ β”‚

Features:
- Keep connection
- Server can send first
- Real-time possible

2. Long Polling​

Client                              Server
β”‚ β”‚
β”‚ 1. Request (Any new data?) β”‚
│──────────────────────────────────>β”‚
β”‚ β”‚
β”‚ Keep connection (Waiting...)
β”‚ β”‚
β”‚ β”‚ No data...
β”‚ β”‚ Still waiting...
β”‚ β”‚
β”‚ β”‚ New data arrived!
β”‚ β”‚
β”‚ 2. Response (Here's the data) β”‚
β”‚<──────────────────────────────────│
β”‚ β”‚
β”‚ Connection closed β”‚
β•³ β•³
β”‚ β”‚
β”‚ 3. Immediately reconnect β”‚
│──────────────────────────────────>β”‚
β”‚ β”‚
β”‚ Waiting again... β”‚

Process:
1. Client β†’ Server: "Any new data?"
2. Server: Wait until data arrives
3. Data arrives β†’ Response
4. Connection closed
5. Immediately reconnect (Repeat)

Pros:
βœ… HTTP-based (Use existing infrastructure)
βœ… No firewall issues
βœ… Simple implementation

Cons:
❌ Inefficient (Continuous reconnection)
❌ High server burden
❌ Header overhead

3. Server-Sent Events (SSE)​

Client                              Server
β”‚ β”‚
β”‚ 1. Connection request β”‚
│──────────────────────────────────>β”‚
β”‚ β”‚
β”‚ 2. Keep connection (Stream starts)β”‚
β”‚<══════════════════════════════════│
β”‚ β”‚
β”‚ 3. Push data β”‚
β”‚<──────────────────────────────────│
β”‚ β”‚
β”‚ 4. Push more data β”‚
β”‚<──────────────────────────────────│
β”‚ β”‚
β”‚ Keep connection... β”‚
β”‚<══════════════════════════════════│

Features:
- Server β†’ Client (One-way)
- Keep connection
- HTTP-based
- Auto reconnection

Pros:
βœ… Simple (Built-in browser)
βœ… Auto reconnection
βœ… Resumable with event ID
βœ… Efficient with HTTP/2

Cons:
❌ One-way (Server β†’ Client)
❌ No binary data
❌ IE not supported

4. WebSocket​

Client                              Server
β”‚ β”‚
β”‚ 1. HTTP Upgrade request β”‚
│──────────────────────────────────>β”‚
β”‚ β”‚
β”‚ 2. Upgrade approved β”‚
β”‚<──────────────────────────────────│
β”‚ β”‚
β”‚ Switch to WebSocket protocol β”‚
β”‚<══════════════════════════════════>β”‚
β”‚ β”‚
β”‚ 3. Send data β”‚
│──────────────────────────────────>β”‚
β”‚ β”‚
β”‚ 4. Receive data β”‚
β”‚<──────────────────────────────────│
β”‚ β”‚
β”‚ 5. Send data β”‚
│──────────────────────────────────>β”‚
β”‚ β”‚
β”‚ Bidirectional communication continues...
β”‚<══════════════════════════════════>β”‚

Features:
- Bidirectional real-time communication
- Separate protocol (ws://, wss://)
- Keep connection
- Low latency

Pros:
βœ… True real-time
βœ… Bidirectional communication
βœ… Low overhead
βœ… Binary support

Cons:
❌ Complex
❌ Possible proxy/firewall issues
❌ Server burden (Keep connection)

πŸ’‘ Real-World Examples​

Long Polling Example​

// ========== Server (Express.js) ==========
const express = require('express');
const app = express();

let messages = [];
let waitingClients = [];

// Add message (called from elsewhere)
function addMessage(message) {
messages.push(message);

// Immediately respond to waiting clients
waitingClients.forEach(client => {
client.json({ messages });
});
waitingClients = [];
}

// Long polling endpoint
app.get('/messages', (req, res) => {
const lastId = parseInt(req.query.lastId) || 0;

// Respond immediately if new messages exist
if (messages.length > lastId) {
return res.json({ messages: messages.slice(lastId) });
}

// Add to waiting list (max 30 seconds)
waitingClients.push(res);

// 30 second timeout
req.setTimeout(30000, () => {
const index = waitingClients.indexOf(res);
if (index > -1) {
waitingClients.splice(index, 1);
res.json({ messages: [] }); // Empty response
}
});
});

app.listen(3000);

// ========== Client ==========
let lastMessageId = 0;

async function longPolling() {
while (true) {
try {
const response = await fetch(`/messages?lastId=${lastMessageId}`);
const data = await response.json();

// Process new messages
if (data.messages.length > 0) {
data.messages.forEach(msg => {
console.log('New message:', msg);
displayMessage(msg);
});
lastMessageId += data.messages.length;
}

// Immediately reconnect
await longPolling();
} catch (error) {
console.error('Error:', error);
// Retry after 3 seconds
await new Promise(resolve => setTimeout(resolve, 3000));
}
}
}

// Start
longPolling();

// ========== Problems ==========
/*
1. Continuous reconnection (inefficient)
2. High network usage
3. High server burden
4. Battery drain (mobile)
*/

Server-Sent Events (SSE) Example​

// ========== Server (Express.js) ==========
const express = require('express');
const app = express();

app.use(express.static('public'));

// SSE endpoint
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');

// CORS (if needed)
res.setHeader('Access-Control-Allow-Origin', '*');

// Connection confirmation message
res.write('data: Connected\n\n');

// Client ID
const clientId = Date.now();
console.log(`Client ${clientId} connected`);

// Send time every 5 seconds
const intervalId = setInterval(() => {
const data = {
time: new Date().toLocaleTimeString(),
message: 'Hello!'
};

// Send in SSE format
res.write(`data: ${JSON.stringify(data)}\n\n`);
}, 5000);

// Handle client disconnection
req.on('close', () => {
console.log(`Client ${clientId} disconnected`);
clearInterval(intervalId);
res.end();
});
});

// Event publishing API
const clients = [];

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

// Save client
clients.push(res);

req.on('close', () => {
const index = clients.indexOf(res);
clients.splice(index, 1);
});
});

// Broadcast message to all clients
app.post('/broadcast', express.json(), (req, res) => {
const { message } = req.body;

clients.forEach(client => {
client.write(`data: ${JSON.stringify({ message })}\n\n`);
});

res.json({ success: true });
});

app.listen(3000);

// ========== Client (HTML) ==========
/*
<!DOCTYPE html>
<html>
<body>
<div id="messages"></div>

<script>
// SSE connection
const eventSource = new EventSource('/events');

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

const div = document.getElementById('messages');
div.innerHTML += `<p>${data.time}: ${data.message}</p>`;
};

// Connection opened
eventSource.onopen = () => {
console.log('SSE connected');
};

// Error handling
eventSource.onerror = (error) => {
console.error('SSE error:', error);
if (eventSource.readyState === EventSource.CLOSED) {
console.log('SSE connection closed');
}
};

// Close connection (when leaving page)
window.addEventListener('beforeunload', () => {
eventSource.close();
});
</script>
</body>
</html>
*/

// ========== Advanced Features ==========

// 1. Specify event type
app.get('/events/typed', (req, res) => {
res.setHeader('Content-Type', 'text/event-stream');

// Multiple types of events
setInterval(() => {
// Regular message
res.write(`event: message\ndata: Hello\n\n`);

// Notification
res.write(`event: notification\ndata: New notification!\n\n`);

// Update
res.write(`event: update\ndata: {"count": 10}\n\n`);
}, 5000);
});

// Client-side handling by event type
/*
eventSource.addEventListener('message', (e) => {
console.log('Message:', e.data);
});

eventSource.addEventListener('notification', (e) => {
console.log('Notification:', e.data);
});

eventSource.addEventListener('update', (e) => {
const data = JSON.parse(e.data);
console.log('Update:', data);
});
*/

// 2. Event ID (Resume on reconnect)
let eventId = 0;

app.get('/events/resumable', (req, res) => {
res.setHeader('Content-Type', 'text/event-stream');

const lastEventId = parseInt(req.headers['last-event-id']) || 0;
console.log('Last event ID:', lastEventId);

// Send only events after lastEventId
setInterval(() => {
eventId++;
res.write(`id: ${eventId}\ndata: Event ${eventId}\n\n`);
}, 1000);
});

// 3. Set retry time
app.get('/events/retry', (req, res) => {
res.setHeader('Content-Type', 'text/event-stream');

// Reconnect after 5 seconds
res.write('retry: 5000\n');
res.write('data: Connected\n\n');
});

WebSocket Example (Socket.io)​

// ========== Server (Socket.io) ==========
const express = require('express');
const http = require('http');
const socketIo = require('socket.io');

const app = express();
const server = http.createServer(app);
const io = socketIo(server, {
cors: {
origin: '*'
}
});

app.use(express.static('public'));

// Manage connected users
const users = new Map();

// WebSocket connection
io.on('connection', (socket) => {
console.log('New user connected:', socket.id);

// Save user information
socket.on('register', (username) => {
users.set(socket.id, { username, socket });
console.log(`${username} registered`);

// Notify all users
io.emit('user-joined', {
username,
totalUsers: users.size
});
});

// Receive message
socket.on('chat-message', (message) => {
const user = users.get(socket.id);
console.log(`${user.username}: ${message}`);

// Broadcast to all users
io.emit('chat-message', {
username: user.username,
message,
timestamp: new Date().toISOString()
});
});

// Typing indicator
socket.on('typing', () => {
const user = users.get(socket.id);
socket.broadcast.emit('user-typing', user.username);
});

socket.on('stop-typing', () => {
const user = users.get(socket.id);
socket.broadcast.emit('user-stop-typing', user.username);
});

// Private message
socket.on('private-message', ({ to, message }) => {
const targetSocket = Array.from(users.values())
.find(u => u.username === to)?.socket;

if (targetSocket) {
const sender = users.get(socket.id);
targetSocket.emit('private-message', {
from: sender.username,
message
});
}
});

// Disconnection
socket.on('disconnect', () => {
const user = users.get(socket.id);
if (user) {
console.log(`${user.username} disconnected`);
users.delete(socket.id);

// Notify all users
io.emit('user-left', {
username: user.username,
totalUsers: users.size
});
}
});
});

server.listen(3000, () => {
console.log('Server running: http://localhost:3000');
});

// ========== Client (HTML + Socket.io) ==========
/*
<!DOCTYPE html>
<html>
<head>
<script src="/socket.io/socket.io.js"></script>
</head>
<body>
<div id="chat">
<div id="messages"></div>
<input id="username" placeholder="Name" />
<input id="message" placeholder="Message" />
<button onclick="sendMessage()">Send</button>
</div>

<script>
// Socket.io connection
const socket = io('http://localhost:3000');

// Connection successful
socket.on('connect', () => {
console.log('Connected:', socket.id);
});

// Register user
function register() {
const username = document.getElementById('username').value;
socket.emit('register', username);
}

// Send message
function sendMessage() {
const message = document.getElementById('message').value;
socket.emit('chat-message', message);
document.getElementById('message').value = '';
}

// Receive message
socket.on('chat-message', (data) => {
const div = document.getElementById('messages');
div.innerHTML += `
<p><strong>${data.username}:</strong> ${data.message}</p>
`;
});

// User joined
socket.on('user-joined', (data) => {
console.log(`${data.username} joined (Total ${data.totalUsers})`);
});

// User left
socket.on('user-left', (data) => {
console.log(`${data.username} left (Total ${data.totalUsers})`);
});

// Typing
let typingTimeout;
document.getElementById('message').addEventListener('input', () => {
socket.emit('typing');

clearTimeout(typingTimeout);
typingTimeout = setTimeout(() => {
socket.emit('stop-typing');
}, 1000);
});

socket.on('user-typing', (username) => {
console.log(`${username} is typing...`);
});

// Disconnection
socket.on('disconnect', () => {
console.log('Disconnected');
});

// Reconnection
socket.on('reconnect', () => {
console.log('Reconnected');
});
</script>
</body>
</html>
*/

Native WebSocket API​

// ========== Server (ws library) ==========
const WebSocket = require('ws');

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

wss.on('connection', (ws) => {
console.log('Client connected');

// Receive message
ws.on('message', (message) => {
console.log('Received message:', message.toString());

// Broadcast to all clients
wss.clients.forEach((client) => {
if (client.readyState === WebSocket.OPEN) {
client.send(message.toString());
}
});
});

// Disconnection
ws.on('close', () => {
console.log('Client disconnected');
});

// Error
ws.on('error', (error) => {
console.error('WebSocket error:', error);
});

// Welcome message
ws.send('Connected to server!');
});

console.log('WebSocket server running: ws://localhost:8080');

// ========== Client (Browser) ==========

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

// Connection opened
ws.addEventListener('open', (event) => {
console.log('WebSocket connected');
ws.send('Hello!');
});

// Receive message
ws.addEventListener('message', (event) => {
console.log('From server:', event.data);
});

// Error
ws.addEventListener('error', (error) => {
console.error('WebSocket error:', error);
});

// Connection closed
ws.addEventListener('close', (event) => {
console.log('WebSocket disconnected');
if (event.code === 1000) {
console.log('Normal closure');
} else {
console.log('Abnormal closure:', event.code);
}
});

// Send message
function sendMessage(message) {
if (ws.readyState === WebSocket.OPEN) {
ws.send(message);
} else {
console.error('WebSocket is not open');
}
}

// Close connection
function closeConnection() {
ws.close(1000, 'Normal closure');
}

// ========== Binary Data Transfer ==========

// File transfer
async function sendFile(file) {
const arrayBuffer = await file.arrayBuffer();
ws.send(arrayBuffer);
}

// Server receives binary
ws.addEventListener('message', (event) => {
if (event.data instanceof ArrayBuffer) {
console.log('Binary data received:', event.data.byteLength, 'bytes');
} else {
console.log('Text data:', event.data);
}
});

// ========== Ping/Pong (Keep connection) ==========

// Server
wss.on('connection', (ws) => {
ws.isAlive = true;

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

// Ping every 30 seconds
setInterval(() => {
wss.clients.forEach((ws) => {
if (ws.isAlive === false) {
return ws.terminate(); // Terminate connection if no response
}

ws.isAlive = false;
ws.ping();
});
}, 30000);

// ========== Auto Reconnection ==========

let ws;
let reconnectAttempts = 0;
const maxReconnectAttempts = 10;

function connect() {
ws = new WebSocket('ws://localhost:8080');

ws.addEventListener('open', () => {
console.log('Connected');
reconnectAttempts = 0;
});

ws.addEventListener('close', (event) => {
console.log('Disconnected');

// Auto reconnect
if (reconnectAttempts < maxReconnectAttempts) {
reconnectAttempts++;
const delay = Math.min(1000 * Math.pow(2, reconnectAttempts), 30000);
console.log(`Reconnecting in ${delay}ms...`);
setTimeout(connect, delay);
} else {
console.error('Reconnection failed');
}
});

ws.addEventListener('error', (error) => {
console.error('Error:', error);
ws.close();
});
}

connect();

πŸ€” Frequently Asked Questions​

Q1. Which method should I choose?​

A:

βœ… Choose Long Polling when:
β”œβ”€ WebSocket/SSE not supported
β”œβ”€ Legacy systems
β”œβ”€ Simple notifications
└─ Example: Old browser support, simple polling

Use cases:
- Stock prices (infrequent updates)
- Email checking
- Simple notifications

βœ… Choose SSE when:
β”œβ”€ Server β†’ Client one-way
β”œβ”€ Real-time updates
β”œβ”€ Want simple implementation
β”œβ”€ Need auto reconnection
└─ Example: News feed, notifications, dashboard

Use cases:
- News feed (real-time updates)
- Stock charts (server push)
- Progress indicators
- Log streaming
- Server monitoring

βœ… Choose WebSocket when:
β”œβ”€ Bidirectional real-time communication
β”œβ”€ Low latency important
β”œβ”€ Frequent message exchange
β”œβ”€ Binary data
└─ Example: Chat, games, collaboration tools

Use cases:
- Chat applications
- Multiplayer games
- Collaborative document editing (Google Docs)
- Video conferencing
- IoT real-time control

πŸ“Š Comparison Table:

Feature | Long Polling | SSE | WebSocket
-----------|--------------|----------|----------
Direction | Bidirectional| One-way | Bidirectional
Protocol | HTTP | HTTP | WebSocket
Latency | High | Low | Very low
Overhead | High | Medium | Low
Browser | All | Most | All
Complexity | Low | Low | High
Reconnect | Manual | Auto | Manual
Binary | Possible | No | Possible

Recommendation:
- Simple real-time: SSE
- Chat/Games: WebSocket
- Legacy support: Long Polling

Q2. What are the performance differences?​

A:

// ========== Benchmark Example ==========

// 1. Long Polling
// Per request:
// - HTTP header: ~800 bytes
// - Reconnection time: ~50ms
// - Server resources: 1 thread per connection

// 100 concurrent connections:
// - Requests per second: 100
// - Data transfer: 80KB/s (headers only)
// - Server load: High

// 2. SSE
// Per connection:
// - HTTP header: Initial only (~800 bytes)
// - Message overhead: ~10 bytes
// - Server resources: Keep-Alive connection

// 100 concurrent connections:
// - Initial connection: 80KB
// - Per message: 1KB (no headers)
// - Server load: Medium

// 3. WebSocket
// Per connection:
// - Upgrade header: Once only (~500 bytes)
// - Frame overhead: 2~6 bytes
// - Server resources: Keep connection

// 100 concurrent connections:
// - Initial connection: 50KB
// - Per message: ~0.002KB (frame only)
// - Server load: Low

// ========== Actual Measurements ==========

// Test: 100 users sending 1 message per second

// Long Polling
const longPollingBenchmark = {
requestsPerSecond: 100,
averageLatency: '200ms',
bandwidth: '8MB/min',
serverCPU: '70%',
memory: '500MB'
};

// SSE
const sseBenchmark = {
requestsPerSecond: 0, // Keep connection
averageLatency: '10ms',
bandwidth: '600KB/min',
serverCPU: '30%',
memory: '200MB'
};

// WebSocket
const webSocketBenchmark = {
requestsPerSecond: 0, // Keep connection
averageLatency: '2ms',
bandwidth: '100KB/min',
serverCPU: '15%',
memory: '100MB'
};

// ========== Real-world Optimization ==========

// 1. Limit connection count
const MAX_CONNECTIONS = 1000;

io.on('connection', (socket) => {
if (io.engine.clientsCount > MAX_CONNECTIONS) {
socket.disconnect();
return;
}
});

// 2. Message compression
const io = socketIo(server, {
perMessageDeflate: {
threshold: 1024 // Compress only above 1KB
}
});

// 3. Batch processing
const messageQueue = [];

setInterval(() => {
if (messageQueue.length > 0) {
io.emit('batch', messageQueue);
messageQueue.length = 0;
}
}, 100); // Batch send every 100ms

// 4. Use rooms
socket.join('room1');
io.to('room1').emit('message', data); // room1 only

// 5. Binary transfer
// WebSocket is efficient with binary
const buffer = Buffer.from('Hello');
socket.send(buffer); // Faster than text

// 6. Optimize heartbeat
const HEARTBEAT_INTERVAL = 25000; // 25 seconds
const HEARTBEAT_TIMEOUT = 30000; // 30 seconds

setInterval(() => {
wss.clients.forEach((ws) => {
if (ws.isAlive === false) {
return ws.terminate();
}
ws.isAlive = false;
ws.ping();
});
}, HEARTBEAT_INTERVAL);

Q3. What about mobile apps?​

A:

// ========== Mobile Considerations ==========

// 1. Battery consumption
// Long Polling: ❌ High (continuous reconnection)
// SSE: ⚠️ Medium
// WebSocket: βœ… Low (keep connection)

// 2. Network switching
// WiFi <-> Mobile data switching

// Auto reconnection
let ws;

function connect() {
ws = new WebSocket('wss://api.example.com');

ws.onclose = () => {
// Exponential backoff
const delay = Math.min(1000 * Math.pow(2, attempts), 30000);
setTimeout(connect, delay);
};
}

// Detect network status
window.addEventListener('online', () => {
console.log('Online - reconnecting');
connect();
});

window.addEventListener('offline', () => {
console.log('Offline');
ws.close();
});

// 3. Background handling
// When app goes to background?

// React Native example
import { AppState } from 'react-native';

AppState.addEventListener('change', (nextAppState) => {
if (nextAppState === 'background') {
// Background β†’ Keep or close connection
ws.close();
} else if (nextAppState === 'active') {
// Foreground β†’ Reconnect
connect();
}
});

// 4. Data saver mode
// Depending on user settings

const isDataSaverMode = await getDataSaverSetting();

if (isDataSaverMode) {
// Long polling (less frequent)
setInterval(poll, 60000); // Every 1 minute
} else {
// WebSocket (real-time)
connect();
}

// 5. Push notification integration
// WebSocket + FCM/APNs combination

// App active: WebSocket
if (appIsActive) {
useWebSocket();
}

// Background: Push notification
if (appIsBackground) {
usePushNotification();
}

// ========== React Native Example ==========
import React, { useEffect, useState } from 'react';
import { View, Text } from 'react-native';

function ChatScreen() {
const [ws, setWs] = useState(null);
const [messages, setMessages] = useState([]);
const [isConnected, setIsConnected] = useState(false);

useEffect(() => {
const websocket = new WebSocket('wss://api.example.com');

websocket.onopen = () => {
console.log('Connected');
setIsConnected(true);
};

websocket.onmessage = (event) => {
const message = JSON.parse(event.data);
setMessages(prev => [...prev, message]);
};

websocket.onerror = (error) => {
console.error('WebSocket error:', error);
};

websocket.onclose = () => {
console.log('Disconnected');
setIsConnected(false);

// Reconnect after 3 seconds
setTimeout(() => {
// Reconnection logic
}, 3000);
};

setWs(websocket);

// Cleanup
return () => {
websocket.close();
};
}, []);

const sendMessage = (text) => {
if (ws && isConnected) {
ws.send(JSON.stringify({ text }));
}
};

return (
<View>
<Text>Connected: {isConnected ? 'Yes' : 'No'}</Text>
{messages.map((msg, i) => (
<Text key={i}>{msg.text}</Text>
))}
</View>
);
}

Q4. How to secure it?​

A:

// ========== 1. Use HTTPS/WSS ==========

// ❌ HTTP/WS (Plain text)
const ws = new WebSocket('ws://api.example.com');

// βœ… HTTPS/WSS (Encrypted)
const ws = new WebSocket('wss://api.example.com');

// ========== 2. Authentication ==========

// Method 1: JWT in URL (Simple but not recommended)
const token = getJWTToken();
const ws = new WebSocket(`wss://api.example.com?token=${token}`);
// ⚠️ Risk of token exposure in URL

// Method 2: Authenticate after connection (Recommended)
const ws = new WebSocket('wss://api.example.com');

ws.onopen = () => {
// Send authentication message
ws.send(JSON.stringify({
type: 'auth',
token: getJWTToken()
}));
};

// Server
io.use((socket, next) => {
const token = socket.handshake.auth.token;

try {
const decoded = jwt.verify(token, SECRET_KEY);
socket.userId = decoded.userId;
next();
} catch (error) {
next(new Error('Authentication failed'));
}
});

// Method 3: Cookie (HttpOnly)
// Client
const ws = new WebSocket('wss://api.example.com');
// Cookie is sent automatically

// Server
io.use((socket, next) => {
const cookies = parseCookies(socket.request.headers.cookie);
const sessionId = cookies.sessionId;

if (isValidSession(sessionId)) {
next();
} else {
next(new Error('Authentication failed'));
}
});

// ========== 3. CORS Configuration ==========

const io = socketIo(server, {
cors: {
origin: 'https://myapp.com', // Specific domain only
credentials: true
}
});

// Multiple domains
const allowedOrigins = ['https://myapp.com', 'https://admin.myapp.com'];

const io = socketIo(server, {
cors: {
origin: (origin, callback) => {
if (allowedOrigins.includes(origin)) {
callback(null, true);
} else {
callback(new Error('CORS denied'));
}
},
credentials: true
}
});

// ========== 4. Rate Limiting ==========

const rateLimitMap = new Map();

io.on('connection', (socket) => {
socket.on('message', (data) => {
const userId = socket.userId;
const now = Date.now();

// Track message count per user
if (!rateLimitMap.has(userId)) {
rateLimitMap.set(userId, []);
}

const userMessages = rateLimitMap.get(userId);

// Filter messages from last minute
const recentMessages = userMessages.filter(
timestamp => now - timestamp < 60000
);

// Limit to 10 per minute
if (recentMessages.length >= 10) {
socket.emit('error', 'Message rate limit exceeded');
return;
}

recentMessages.push(now);
rateLimitMap.set(userId, recentMessages);

// Process message
handleMessage(data);
});
});

// ========== 5. Input Validation ==========

socket.on('message', (data) => {
// Type validation
if (typeof data !== 'object') {
return socket.emit('error', 'Invalid data format');
}

// Required field validation
if (!data.type || !data.content) {
return socket.emit('error', 'Missing required fields');
}

// Length validation
if (data.content.length > 1000) {
return socket.emit('error', 'Message too long');
}

// XSS prevention
const sanitizedContent = sanitizeHtml(data.content);

// Process
broadcastMessage(sanitizedContent);
});

// ========== 6. Namespace and Room ==========

// Isolate by namespace
const chatNamespace = io.of('/chat');
const adminNamespace = io.of('/admin');

chatNamespace.on('connection', (socket) => {
// Regular chat
});

adminNamespace.use(authenticateAdmin);
adminNamespace.on('connection', (socket) => {
// Admin only
});

// Access control with rooms
socket.on('join-room', (roomId) => {
if (canAccessRoom(socket.userId, roomId)) {
socket.join(roomId);
} else {
socket.emit('error', 'No room access permission');
}
});

// ========== 7. DDoS Prevention ==========

// Limit connections per IP
const MAX_CONNECTIONS_PER_IP = 5;
const connectionsByIP = new Map();

io.on('connection', (socket) => {
const ip = socket.handshake.address;

const count = connectionsByIP.get(ip) || 0;

if (count >= MAX_CONNECTIONS_PER_IP) {
socket.disconnect();
return;
}

connectionsByIP.set(ip, count + 1);

socket.on('disconnect', () => {
const newCount = connectionsByIP.get(ip) - 1;
if (newCount <= 0) {
connectionsByIP.delete(ip);
} else {
connectionsByIP.set(ip, newCount);
}
});
});

Q5. How to handle errors?​

A:

// ========== Client Error Handling ==========

class WebSocketClient {
constructor(url) {
this.url = url;
this.ws = null;
this.reconnectAttempts = 0;
this.maxReconnectAttempts = 10;
this.reconnectDelay = 1000;
}

connect() {
try {
this.ws = new WebSocket(this.url);

this.ws.onopen = () => {
console.log('Connection successful');
this.reconnectAttempts = 0;
this.onConnected();
};

this.ws.onmessage = (event) => {
try {
const data = JSON.parse(event.data);
this.onMessage(data);
} catch (error) {
console.error('Message parsing error:', error);
}
};

this.ws.onerror = (error) => {
console.error('WebSocket error:', error);
this.onError(error);
};

this.ws.onclose = (event) => {
console.log('Connection closed:', event.code, event.reason);

// Normal closure (1000)
if (event.code === 1000) {
console.log('Normal closure');
return;
}

// Attempt reconnection
this.reconnect();
};
} catch (error) {
console.error('Connection failed:', error);
this.reconnect();
}
}

reconnect() {
if (this.reconnectAttempts >= this.maxReconnectAttempts) {
console.error('Reconnection abandoned');
this.onReconnectFailed();
return;
}

this.reconnectAttempts++;
const delay = Math.min(
this.reconnectDelay * Math.pow(2, this.reconnectAttempts),
30000
);

console.log(`Reconnecting in ${delay}ms (${this.reconnectAttempts}/${this.maxReconnectAttempts})`);

setTimeout(() => {
this.connect();
}, delay);
}

send(data) {
if (this.ws && this.ws.readyState === WebSocket.OPEN) {
try {
this.ws.send(JSON.stringify(data));
} catch (error) {
console.error('Send error:', error);
}
} else {
console.error('WebSocket is not open');
// Save to message queue
this.queueMessage(data);
}
}

close() {
if (this.ws) {
this.ws.close(1000, 'Client closing');
}
}

// Event handlers (override)
onConnected() {}
onMessage(data) {}
onError(error) {}
onReconnectFailed() {}

queueMessage(data) {
// Implement offline message queue
}
}

// Usage
const client = new WebSocketClient('wss://api.example.com');

client.onConnected = () => {
console.log('Connected!');
};

client.onMessage = (data) => {
console.log('Message:', data);
};

client.onError = (error) => {
console.error('Error occurred:', error);
// Show error in UI
showErrorNotification('Connection error');
};

client.onReconnectFailed = () => {
// Handle reconnection failure
showErrorModal('Cannot connect to server');
};

client.connect();

// ========== Server Error Handling ==========

io.on('connection', (socket) => {
// Error handler
socket.on('error', (error) => {
console.error('Socket error:', error);
});

// Message processing error
socket.on('message', async (data) => {
try {
// Input validation
if (!isValidMessage(data)) {
throw new Error('Invalid message');
}

// Process
await processMessage(data);
} catch (error) {
console.error('Message processing error:', error);

// Send error to client
socket.emit('error', {
code: 'MESSAGE_PROCESSING_ERROR',
message: error.message
});
}
});

// Handle disconnection
socket.on('disconnect', (reason) => {
console.log('Disconnected:', reason);

// Clean up user
cleanupUser(socket.userId);

// Notify other users
socket.broadcast.emit('user-left', socket.userId);
});
});

// Global error handler
io.engine.on('connection_error', (error) => {
console.error('Connection error:', error);
});

// ========== Timeout Handling ==========

socket.on('message', (data, callback) => {
// Set timeout (5 seconds)
const timeout = setTimeout(() => {
callback({
error: 'TIMEOUT',
message: 'Response timeout'
});
}, 5000);

// Process
processMessage(data)
.then(result => {
clearTimeout(timeout);
callback({ success: true, data: result });
})
.catch(error => {
clearTimeout(timeout);
callback({ error: error.message });
});
});

πŸŽ“ Next Steps​

Now that you understand real-time communication, learn more:

  1. HTTP Basics (Document coming soon) - Understanding HTTP protocol
  2. What is API? (Document coming soon) - Basic API concepts
  3. REST API vs GraphQL - API design

Try It Yourself​

# ========== 1. Socket.io Chat App ==========

mkdir chat-app
cd chat-app
npm init -y
npm install express socket.io

# After writing server.js
node server.js

# ========== 2. SSE Real-time Notifications ==========

mkdir sse-demo
cd sse-demo
npm install express

# Write server.js
# Write index.html

node server.js
# Visit http://localhost:3000

# ========== 3. WebSocket Game ==========

mkdir websocket-game
cd websocket-game
npm install ws express

# Implement multiplayer game
node server.js

🎬 Conclusion​

Real-time communication is a core technology of the modern web:

  • Long Polling: Legacy support, HTTP-based
  • SSE: Server push, simple implementation
  • WebSocket: Bidirectional real-time, best performance
  • Selection Criteria: Requirements, environment, complexity

Choose the right real-time communication method for your project and create an excellent user experience! πŸ”΄