WebSockets and Real-time Communication
WebSockets enable bidirectional, full-duplex communication between clients and servers. Learn to build real-time applications with persistent connections.
Challenge Statement
Section titled “Challenge Statement”Build a real-time communication system using WebSockets that maintains persistent connections, handles multiple clients, and broadcasts events efficiently.
Explanation
Section titled “Explanation”WebSockets vs HTTP
Section titled “WebSockets vs HTTP”HTTP: Request-response, stateless, polling required WebSockets: Bidirectional, stateful, push notifications
Key Concepts
Section titled “Key Concepts”Connection Upgrade: HTTP upgrade to WebSocket protocol Handshake: Initial connection negotiation Frames: Data units sent over connection Events: Message, open, close, error events Namespaces: Logical grouping of connections
Code Examples
Section titled “Code Examples”1. Basic WebSocket Server
Section titled “1. Basic WebSocket Server”const express = require('express');const WebSocket = require('ws');const http = require('http');
const app = express();const server = http.createServer(app);const wss = new WebSocket.Server({ server });
wss.on('connection', (ws) => { console.log('Client connected');
// Send welcome message ws.send(JSON.stringify({ type: 'welcome', message: 'Connected to server' }));
// Handle messages from client ws.on('message', (data) => { try { const message = JSON.parse(data); console.log('Received:', message);
// Echo to all clients wss.clients.forEach((client) => { if (client.readyState === WebSocket.OPEN) { client.send(JSON.stringify({ type: 'message', data: message, timestamp: new Date(), })); } }); } catch (error) { ws.send(JSON.stringify({ type: 'error', error: error.message })); } });
// Handle client disconnect ws.on('close', () => { console.log('Client disconnected'); });
// Handle errors ws.on('error', (error) => { console.error('WebSocket error:', error); });});
server.listen(3000, () => { console.log('WebSocket server running on port 3000');});2. Socket.io with Rooms
Section titled “2. Socket.io with Rooms”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: '*' },});
io.on('connection', (socket) => { console.log('User connected:', socket.id);
// Join a room socket.on('join-room', (roomId) => { socket.join(roomId); io.to(roomId).emit('user-joined', { userId: socket.id, message: 'A user joined the room', }); });
// Send message to room socket.on('message', (roomId, message) => { io.to(roomId).emit('message', { userId: socket.id, message, timestamp: new Date(), }); });
// Typing indicator socket.on('typing', (roomId) => { socket.to(roomId).emit('user-typing', { userId: socket.id }); });
// Leave room socket.on('leave-room', (roomId) => { socket.leave(roomId); io.to(roomId).emit('user-left', { userId: socket.id, message: 'User left the room', }); });
socket.on('disconnect', () => { console.log('User disconnected:', socket.id); });});
app.get('/', (req, res) => { res.sendFile(__dirname + '/index.html');});
server.listen(3000);3. Broadcast and Targeted Messaging
Section titled “3. Broadcast and Targeted Messaging”// Broadcast to all connected clientsio.emit('notification', { message: 'Server maintenance in 5 minutes' });
// Broadcast to all except sendersocket.broadcast.emit('user-joined', { userId: socket.id });
// Send to specific userio.to(userId).emit('private-message', { from: socket.id, message: 'Hello!',});
// Send to roomio.to(roomId).emit('room-update', { data: 'Updated data' });
// Send to room except sendersocket.to(roomId).emit('message', { message: 'New message' });
// Send acknowledgmentsocket.on('message', (data, callback) => { console.log('Message received:', data); callback({ status: 'received' });});4. Presence and Activity Tracking
Section titled “4. Presence and Activity Tracking”class PresenceManager { constructor(io) { this.io = io; this.activeUsers = new Map(); }
addUser(socketId, userId, userData) { this.activeUsers.set(socketId, { userId, ...userData, lastActive: Date.now(), });
// Broadcast online status this.io.emit('user-online', { userId, onlineCount: this.activeUsers.size, }); }
removeUser(socketId) { const user = this.activeUsers.get(socketId); if (user) { this.activeUsers.delete(socketId);
this.io.emit('user-offline', { userId: user.userId, onlineCount: this.activeUsers.size, }); } }
getActiveUsers() { return Array.from(this.activeUsers.values()); }
isUserOnline(userId) { return Array.from(this.activeUsers.values()).some(u => u.userId === userId); }}
const presenceManager = new PresenceManager(io);
io.on('connection', (socket) => { socket.on('user-login', (userData) => { presenceManager.addUser(socket.id, userData.userId, userData); });
socket.on('disconnect', () => { presenceManager.removeUser(socket.id); });});5. Real-time Notifications
Section titled “5. Real-time Notifications”class NotificationManager { constructor(io) { this.io = io; }
async sendNotification(userId, notification) { this.io.to(userId).emit('notification', { id: Date.now().toString(), type: notification.type, message: notification.message, data: notification.data, timestamp: new Date(), read: false, }); }
async sendToUsers(userIds, notification) { userIds.forEach(userId => { this.sendNotification(userId, notification); }); }
async broadcastNotification(notification) { this.io.emit('notification', { ...notification, timestamp: new Date(), }); }}
const notificationManager = new NotificationManager(io);
// UsagenotificationManager.sendNotification('user-123', { type: 'order-status', message: 'Your order has been shipped', data: { orderId: 'order-456' },});6. Heartbeat and Connection Management
Section titled “6. Heartbeat and Connection Management”const HEARTBEAT_INTERVAL = 30000; // 30 secondsconst HEARTBEAT_TIMEOUT = 60000; // 60 seconds
class ConnectionManager { constructor(io) { this.io = io; this.heartbeats = new Map(); }
startHeartbeat(socket) { socket.emit('ping', { timestamp: Date.now() });
socket.on('pong', (data) => { this.heartbeats.set(socket.id, Date.now()); });
// Timeout check const timeout = setInterval(() => { const lastHeartbeat = this.heartbeats.get(socket.id);
if (!lastHeartbeat || Date.now() - lastHeartbeat > HEARTBEAT_TIMEOUT) { socket.disconnect(); clearInterval(timeout); } }, HEARTBEAT_INTERVAL); }
isHealthy(socketId) { const lastHeartbeat = this.heartbeats.get(socketId); return lastHeartbeat && Date.now() - lastHeartbeat < HEARTBEAT_TIMEOUT; }}
const connectionManager = new ConnectionManager(io);
io.on('connection', (socket) => { connectionManager.startHeartbeat(socket);});Best Practices
Section titled “Best Practices”-
Connection Management
- Implement heartbeats to detect stale connections
- Handle reconnection gracefully
- Set appropriate timeouts
- Clean up resources on disconnect
-
Scalability
- Use Redis adapter for multi-server deployments
- Implement connection pooling
- Monitor memory usage
- Use message queues for high-volume events
-
Security
- Authenticate all connections
- Validate all incoming messages
- Use HTTPS/WSS in production
- Implement rate limiting
-
Performance
- Minimize message payload
- Use binary protocols when appropriate
- Implement message batching
- Monitor bandwidth usage
-
Reliability
- Handle network failures gracefully
- Implement automatic reconnection
- Log all errors
- Test failure scenarios
Real-World Applications
Section titled “Real-World Applications”- Chat Applications: Real-time messaging
- Collaborative Tools: Live editing and cursor tracking
- Live Notifications: Instant alerts and updates
- Gaming: Real-time multiplayer interactions
- Dashboards: Live data updates and monitoring