Skip to content
Plurafy

WebSockets and Real-time Communication

WebSockets enable bidirectional, full-duplex communication between clients and servers. Learn to build real-time applications with persistent connections.

Build a real-time communication system using WebSockets that maintains persistent connections, handles multiple clients, and broadcasts events efficiently.

HTTP: Request-response, stateless, polling required WebSockets: Bidirectional, stateful, push notifications

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

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');
});
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);
// Broadcast to all connected clients
io.emit('notification', { message: 'Server maintenance in 5 minutes' });
// Broadcast to all except sender
socket.broadcast.emit('user-joined', { userId: socket.id });
// Send to specific user
io.to(userId).emit('private-message', {
from: socket.id,
message: 'Hello!',
});
// Send to room
io.to(roomId).emit('room-update', { data: 'Updated data' });
// Send to room except sender
socket.to(roomId).emit('message', { message: 'New message' });
// Send acknowledgment
socket.on('message', (data, callback) => {
console.log('Message received:', data);
callback({ status: 'received' });
});
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);
});
});
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);
// Usage
notificationManager.sendNotification('user-123', {
type: 'order-status',
message: 'Your order has been shipped',
data: { orderId: 'order-456' },
});
const HEARTBEAT_INTERVAL = 30000; // 30 seconds
const 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);
});
  1. Connection Management

    • Implement heartbeats to detect stale connections
    • Handle reconnection gracefully
    • Set appropriate timeouts
    • Clean up resources on disconnect
  2. Scalability

    • Use Redis adapter for multi-server deployments
    • Implement connection pooling
    • Monitor memory usage
    • Use message queues for high-volume events
  3. Security

    • Authenticate all connections
    • Validate all incoming messages
    • Use HTTPS/WSS in production
    • Implement rate limiting
  4. Performance

    • Minimize message payload
    • Use binary protocols when appropriate
    • Implement message batching
    • Monitor bandwidth usage
  5. Reliability

    • Handle network failures gracefully
    • Implement automatic reconnection
    • Log all errors
    • Test failure scenarios
  • 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