Siksha Sarovar

Siksha Sarovar (sikshasarovar.com) is a free educational web application that helps students in India learn programming and prepare for academic and competitive exams. The platform offers structured coding courses (C, C++, Python, Java, HTML, CSS, PHP, Power BI, AI, Machine Learning, Data Science), complete university curriculum notes for BCA/MCA students with previous year question papers, Class 10 and Class 12 CBSE/HBSE school notes, and dedicated preparation material for SSC, UPSC, Banking, Railway and other government exams. Browsing the site is completely free and requires no account. Users may optionally sign in with Google solely to save their learning progress, quiz scores and personal preferences across devices.

Privacy Policy | Terms of Service | Contact Siksha Sarovar | About Siksha Sarovar

v4.0.9 · PWA
Siksha Sarovar logo
Siksha Sarovar
Your Learning Universe

Siksha Sarovar is a free e-learning platform for coding courses, BCA university notes and competitive exam preparation. Optional Google sign-in saves your learning progress across devices.

Initializing knowledge base…
Compiling modules 0%

23. Socket.io — What You Learn from Socket.io

Lesson 23 of 23 in the free Backend Development notes on Siksha Sarovar, written by Rohit Jangra.

Core Concepts Recap

Building real-time features with Socket.io teaches you fundamentals of event-driven architecture that apply far beyond chat apps. Every system you build that needs live updates — dashboards, collaborative tools, multiplayer games, live notifications — uses these same patterns.

---

Client-Side Socket.io with React

// React component: connecting to Socket.io
import { io, Socket } from 'socket.io-client';
import { useEffect, useRef, useState } from 'react';

function ChatRoom({ roomId, userId, username }: Props) {
  const socketRef = useRef<Socket | null>(null);
  const [messages, setMessages] = useState<Message[]>([]);
  const [onlineUsers, setOnlineUsers] = useState(0);

  useEffect(() => {
    // Create socket connection on mount
    socketRef.current = io(import.meta.env.VITE_API_BASE_URL, {
      withCredentials: true,
    });

    const socket = socketRef.current;

    socket.on('connect', () => {
      console.log('Connected:', socket.id);
      socket.emit('joinRoom', { roomId, userId, username });
    });

    socket.on('receiveMessage', (msg: Message) => {
      setMessages((prev) => [...prev, msg]);
    });

    socket.on('onlineUsers', (count: number) => {
      setOnlineUsers(count);
    });

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

    socket.on('connect_error', (err) => {
      console.error('Connection error:', err.message);
    });

    // Cleanup on unmount
    return () => {
      socket.disconnect();
    };
  }, [roomId]);

  const sendMessage = (text: string) => {
    socketRef.current?.emit('sendMessage', { roomId, message: text });
  };

  return (
    <div>
      <p>Online: {onlineUsers}</p>
      {messages.map((m) => <div key={m.id}>{m.username}: {m.message}</div>)}
    </div>
  );
}

---

Custom useSocket Hook

Building a reusable useSocket hook keeps socket logic out of UI components:

// hooks/useSocket.ts
import { useEffect, useRef } from 'react';
import { io, Socket } from 'socket.io-client';

export const useSocket = (url: string) => {
  const socketRef = useRef<Socket | null>(null);

  if (!socketRef.current) {
    socketRef.current = io(url, {
      withCredentials: true,
      autoConnect: false, // Don't connect until we call connect()
    });
  }

  useEffect(() => {
    const socket = socketRef.current!;
    socket.connect();

    return () => {
      socket.disconnect();
    };
  }, []);

  return socketRef.current;
};

---

Error Handling

socket.on('connect_error', (err) => {
  console.error('Connection failed:', err.message);
  // err.message values: 'xhr poll error', 'websocket error', 'timeout'
  // Could show a "Reconnecting..." UI
});

socket.on('error', (err) => {
  console.error('Socket error:', err);
});

// Manual reconnection:
socket.disconnect();
socket.connect();

---

Acknowledgements

Acknowledgements confirm that a message was received by the server:

// Client: emit with callback
socket.emit('sendMessage', { roomId, message }, (response: { success: boolean; id: string }) => {
  if (response.success) {
    // Message confirmed saved by server
    console.log('Message saved with id:', response.id);
  }
});

// Server: call the callback function
socket.on('sendMessage', async ({ roomId, message }, callback) => {
  const saved = await Message.create({ roomId, message, sender: socket.data.userId });
  io.to(roomId).emit('receiveMessage', saved);
  callback({ success: true, id: saved._id.toString() });
});

---

Volatile Events

Volatile events are dropped if the socket is not currently connected:

// Good for non-critical real-time data (typing indicators, cursor positions)
socket.volatile.emit('cursorPosition', { x: 100, y: 200 });
// If disconnected momentarily, this is dropped — doesn't queue up

---

Scaling Socket.io — The Challenge

A single Node.js process has one event loop. If you run multiple Node.js processes (for load balancing or to use multiple CPU cores), each process has its own in-memory state. Socket.io rooms are in-memory — socket in Process 1 cannot send to room that socket in Process 2 joined.

Solution: Redis Adapter

npm install @socket.io/redis-adapter redis
import { createAdapter } from '@socket.io/redis-adapter';
import { createClient } from 'redis';

const pubClient = createClient({ url: process.env.REDIS_URL });
const subClient = pubClient.duplicate();

await Promise.all([pubClient.connect(), subClient.connect()]);
io.adapter(createAdapter(pubClient, subClient));
// Now all processes share room state via Redis pub/sub

---

Real-World Projects Built with Socket.io

ProjectSocket.io Features Used
Live Chat AppRooms, typing indicators, message delivery, online status
Collaborative Text EditorReal-time text sync, cursor positions, user presence
Multiplayer Tic-Tac-ToeRooms (game sessions), game state sync, turn management
Real-Time DashboardBroadcasting live metrics, server push (no polling)
Push NotificationsPer-user namespaces, broadcast to specific user
Live AuctionBid updates broadcast to all participants in a room

---

Interview Questions on Socket.io

Q1: What is the difference between WebSocket and Socket.io? WebSocket is the underlying browser API/protocol. Socket.io is a library built on top of WebSocket that adds polling fallback, rooms, namespaces, auto-reconnect, and event system.

Q2: How do you scale Socket.io across multiple servers? Use the Redis adapter (@socket.io/redis-adapter) which uses Redis pub/sub to share events across all instances.

Q3: What are rooms and namespaces? Rooms are arbitrary groups of sockets within a namespace. A socket can join multiple rooms. Namespaces are separate channels on the same server (like having multiple apps on one server).

Q4: What are volatile events? When would you use them? Volatile events are dropped if the socket is temporarily disconnected. Use them for non-critical, high-frequency data like typing indicators or mouse cursor positions where losing occasional updates is acceptable.

Q5: How do you prevent a user from joining someone else's private room? Authenticate the socket connection using JWT and validate room access in the joinRoom event handler before calling socket.join():

socket.on('joinRoom', async ({ roomId }) => {
  const hasAccess = await checkRoomAccess(socket.data.userId, roomId);
  if (!hasAccess) return socket.emit('error', 'Access denied');
  socket.join(roomId);
});

---

Socket.io Event-Driven Architecture Lessons

Learning Socket.io teaches you to think in events rather than request-response:

  • Events are named: 'sendMessage', 'joinRoom', 'typing'
  • Both client and server emit and listen to events
  • State is maintained in server memory (rooms, user data)
  • Side effects happen asynchronously (DB saves, notifications)

These patterns are the foundation for understanding event-driven microservices (Kafka, RabbitMQ) and reactive systems — one of the most sought-after architectural skills in senior backend roles.