WebSockets & Real-Time Communication

📖 Concept

WebSockets enable bidirectional, full-duplex communication between client and server over a single TCP connection. Unlike HTTP (request → response), WebSockets allow the server to push data to clients at any time.

WebSocket vs HTTP:

Feature HTTP WebSocket
Direction Client → Server Bidirectional
Connection New for each request Persistent
Overhead Headers per request Minimal after handshake
Latency Higher (new connection) Low (persistent)
Use case REST APIs, page loads Chat, live updates, games

Server-Sent Events (SSE) vs WebSockets:

Feature SSE WebSocket
Direction Server → Client only Bidirectional
Protocol HTTP Custom (ws://)
Reconnection Automatic Manual
Browser support All modern browsers All modern browsers
Use case Live feeds, notifications Chat, collaboration, games

Socket.IO is the most popular WebSocket library for Node.js. It adds:

  • Automatic reconnection
  • Room-based broadcasting
  • Binary data support
  • Fallback to HTTP long-polling
  • Acknowledgements (callback on message delivery)

🏠 Real-world analogy: HTTP is like sending letters — you send a request, wait for a reply. WebSockets are like a phone call — once connected, either party can speak at any time, and the line stays open. Socket.IO is like a conference call app — it handles reconnection, group calls (rooms), and ensures delivery.

💻 Code Example

codeTap to expand ⛶
1// WebSockets with Socket.IO — Real-Time Chat
2
3const express = require("express");
4const { createServer } = require("http");
5const { Server } = require("socket.io");
6
7const app = express();
8const httpServer = createServer(app);
9const io = new Server(httpServer, {
10 cors: { origin: "*", methods: ["GET", "POST"] },
11 pingTimeout: 60000,
12 pingInterval: 25000,
13});
14
15app.use(express.static("public"));
16
17// Track online users
18const onlineUsers = new Map();
19
20// 1. Connection handling
21io.on("connection", (socket) => {
22 console.log(`User connected: ${socket.id}`);
23
24 // 2. User joins with username
25 socket.on("user:join", (username) => {
26 onlineUsers.set(socket.id, { username, joinedAt: Date.now() });
27 socket.username = username;
28
29 // Broadcast to all clients
30 io.emit("user:online", {
31 users: Array.from(onlineUsers.values()),
32 count: onlineUsers.size,
33 });
34
35 // Notify others
36 socket.broadcast.emit("system:message", {
37 text: `${username} joined the chat`,
38 timestamp: Date.now(),
39 });
40 });
41
42 // 3. Chat messages
43 socket.on("chat:message", (data) => {
44 const message = {
45 id: Date.now().toString(),
46 text: data.text,
47 username: socket.username,
48 timestamp: Date.now(),
49 };
50
51 // Send to everyone (including sender)
52 io.emit("chat:message", message);
53 });
54
55 // 4. Rooms (channels)
56 socket.on("room:join", (roomName) => {
57 socket.join(roomName);
58 socket.to(roomName).emit("system:message", {
59 text: `${socket.username} joined #${roomName}`,
60 room: roomName,
61 });
62 });
63
64 socket.on("room:message", ({ room, text }) => {
65 io.to(room).emit("chat:message", {
66 id: Date.now().toString(),
67 text,
68 username: socket.username,
69 room,
70 timestamp: Date.now(),
71 });
72 });
73
74 socket.on("room:leave", (roomName) => {
75 socket.leave(roomName);
76 });
77
78 // 5. Typing indicator
79 socket.on("user:typing", (data) => {
80 socket.broadcast.emit("user:typing", {
81 username: socket.username,
82 isTyping: data.isTyping,
83 });
84 });
85
86 // 6. Acknowledgements (delivery confirmation)
87 socket.on("chat:private", (data, callback) => {
88 const { to, text } = data;
89 const targetSocket = [...io.sockets.sockets.values()].find(
90 (s) => s.username === to
91 );
92
93 if (targetSocket) {
94 targetSocket.emit("chat:private", {
95 from: socket.username,
96 text,
97 timestamp: Date.now(),
98 });
99 callback({ delivered: true });
100 } else {
101 callback({ delivered: false, reason: "User not online" });
102 }
103 });
104
105 // 7. Disconnection
106 socket.on("disconnect", (reason) => {
107 console.log(`User disconnected: ${socket.username} (${reason})`);
108 onlineUsers.delete(socket.id);
109
110 io.emit("user:online", {
111 users: Array.from(onlineUsers.values()),
112 count: onlineUsers.size,
113 });
114
115 if (socket.username) {
116 io.emit("system:message", {
117 text: `${socket.username} left the chat`,
118 timestamp: Date.now(),
119 });
120 }
121 });
122});
123
124// 8. Server-Sent Events (SSE) endpoint — simpler alternative
125app.get("/api/events", (req, res) => {
126 res.writeHead(200, {
127 "Content-Type": "text/event-stream",
128 "Cache-Control": "no-cache",
129 Connection: "keep-alive",
130 });
131
132 const intervalId = setInterval(() => {
133 const data = JSON.stringify({ timestamp: Date.now(), message: "heartbeat" });
134 res.write(`data: ${data}\n\n`);
135 }, 5000);
136
137 req.on("close", () => {
138 clearInterval(intervalId);
139 });
140});
141
142const PORT = process.env.PORT || 3000;
143httpServer.listen(PORT, () => {
144 console.log(`Server running on http://localhost:${PORT}`);
145});

🏋️ Practice Exercise

Exercises:

  1. Build a real-time chat application with Socket.IO — support usernames, message history, and online user list
  2. Implement chat rooms (channels) with join/leave and room-specific messaging
  3. Add typing indicators that show when other users are typing
  4. Implement private messaging with delivery acknowledgements
  5. Build a real-time notification system using Server-Sent Events (SSE)
  6. Scale Socket.IO horizontally across multiple servers using the Redis adapter

⚠️ Common Mistakes

  • Not implementing reconnection logic — network disruptions are common; Socket.IO handles this automatically, but raw WebSocket requires manual reconnection

  • Broadcasting to all sockets including the sender — use socket.broadcast.emit() (excludes sender) vs io.emit() (includes sender) correctly

  • Not authenticating WebSocket connections — validate tokens in the handshake middleware, not after connection

  • Storing WebSocket state only in memory — when scaling to multiple servers, use Redis adapter for Socket.IO to share state

  • Using WebSockets for everything — simple one-way updates are better served by SSE (simpler, auto-reconnect); use WebSockets only when bidirectional communication is needed

💼 Interview Questions

🎤 Mock Interview

Mock interview is powered by AI for WebSockets & Real-Time Communication. Login to unlock this feature.