Part 16 – Real-Time Communication with Socket.io

Socket.io enables real-time, bidirectional communication between web clients and servers. This guide covers everything from basic setup to advanced patterns for production applications.

1. WebSocket Fundamentals

How WebSockets Work

  • Persistent connection between client/server
  • Low latency communication
  • Full-duplex (simultaneous two-way)
  • Event-driven architecture

Socket.io Features

  • Automatic reconnection
  • Room support
  • Binary streaming
  • Fallback to HTTP long-polling
  • Broadcasting capabilities

2. Basic Setup

Server Implementation

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, {
    cors: {
        origin: "https://yourdomain.com",
        methods: ["GET", "POST"]
    }
});

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

    socket.on('disconnect', () => {
        console.log('Client disconnected:', socket.id);
    });
});

server.listen(3000, () => {
    console.log('Server running with Socket.io');
});

Client Implementation

<script src="/socket.io/socket.io.js"></script>
<script>
    const socket = io('https://yourdomain.com', {
        transports: ['websocket'],
        upgrade: false
    });

    socket.on('connect', () => {
        console.log('Connected to server');
    });

    socket.on('disconnect', () => {
        console.log('Disconnected from server');
    });
</script>

3. Core Communication Patterns

Basic Messaging

// Server
socket.on('chat message', (msg) => {
    console.log('Message received:', msg);
    io.emit('chat message', msg); // Broadcast to all
});

// Client
socket.emit('chat message', 'Hello world!');
socket.on('chat message', (msg) => {
    console.log('New message:', msg);
});

Acknowledgment

// Server
socket.on('update settings', (data, callback) => {
    try {
        // Process data
        callback({ status: 'success' });
    } catch (err) {
        callback({ status: 'error', message: err.message });
    }
});

// Client
socket.emit('update settings', { theme: 'dark' }, (response) => {
    console.log('Server response:', response);
});

4. Rooms and Namespaces

Room Implementation

// Join room
socket.on('join room', (roomId) => {
    socket.join(roomId);
    console.log(`Socket ${socket.id} joined room ${roomId}`);
});

// Leave room
socket.on('leave room', (roomId) => {
    socket.leave(roomId);
});

// Broadcast to room
io.to('room1').emit('room message', 'Hello room!');

// Broadcast to all in room except sender
socket.to('room1').emit('room message', 'Hello others!');

Namespaces

// Create admin namespace
const adminIO = io.of('/admin');

adminIO.use((socket, next) => {
    // Authentication middleware
    if (socket.handshake.auth.token === 'admin-secret') {
        next();
    } else {
        next(new Error('Not authorized'));
    }
});

adminIO.on('connection', (socket) => {
    console.log('Admin connected');
});

5. Advanced Patterns

State Management

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

io.on('connection', (socket) => {
    socket.on('register user', (userId) => {
        users.set(socket.id, userId);
        console.log(`User ${userId} connected`);
    });

    socket.on('disconnect', () => {
        const userId = users.get(socket.id);
        users.delete(socket.id);
        console.log(`User ${userId} disconnected`);
    });
});

Binary Streaming

// Sending files
socket.on('send file', (fileData) => {
    // fileData could be ArrayBuffer, Blob, etc.
    socket.broadcast.emit('receive file', fileData);
});

// With progress events
const stream = fs.createReadStream('large-file.zip');
stream.on('data', (chunk) => {
    socket.emit('file chunk', chunk);
});
stream.on('end', () => {
    socket.emit('file end');
});

6. Scaling Considerations

Multiple Servers Setup

const { createServer } = require('http');
const { Server } = require('socket.io');
const redisAdapter = require('socket.io-redis');

const io = new Server();

// Configure Redis adapter
io.adapter(redisAdapter({
    host: 'redis-server',
    port: 6379
}));

// Behind load balancer
io.attach(createServer(), {
    pingInterval: 10000,
    pingTimeout: 5000,
    cookie: false
});

Load Balancing Requirements

  • Sticky sessions required
  • Configure WebSocket support in LB
  • Health checks for long-lived connections
  • Monitor connection counts per server

7. Security Best Practices

Authentication

// Middleware approach
io.use((socket, next) => {
    const token = socket.handshake.auth.token;
    if (verifyToken(token)) {
        socket.user = decodeToken(token);
        next();
    } else {
        next(new Error('Authentication error'));
    }
});

// On connection
io.on('connection', (socket) => {
    console.log('Authenticated user:', socket.user.id);
});

Rate Limiting

const rateLimit = require('socket.io-rate-limiter');

io.use(rateLimit({
    windowMs: 60 * 1000, // 1 minute
    delayAfter: 20,      // 20 messages
    delayMs: 1000,       // 1s delay after limit
    max: 100             // Max 100 messages/min
}));

// Per-event rate limiting
socket.on('chat message', rateLimit({
    windowMs: 10000,
    max: 5
}), (msg) => {
    // Handler
});

Next: Microservices Architecture →

Socket.io Checklist

  • ✅ Implement proper authentication
  • ✅ Use rooms for targeted communication
  • ✅ Handle disconnections gracefully
  • ✅ Add rate limiting for public events
  • ✅ Plan for scaling with Redis adapter
  • ✅ Monitor connection counts and events

Leave a Comment

Your email address will not be published. Required fields are marked *