1. Basics
1. β What are WebSockets?
WebSockets provide a full-duplex, persistent communication channel over a single TCP connection between a client (like a browser) and a server.
π Key Features of WebSockets:
- Real-time Communication: Ideal for chat apps, games, live notifications.
- Persistent Connection: Unlike HTTP, where a new request is made every time, WebSocket stays open.
- Bidirectional: Server can push messages to client without client requesting them.
- Lightweight: Less overhead compared to repeated HTTP polling or long polling.
π§° What is Socket.IO?
Socket.IO is a JavaScript library that simplifies using WebSockets by:
- Adding fallbacks (like long polling) if WebSocket isnβt supported.
- Handling reconnections, event handling, and rooms/channels.
2. π οΈ Comprehensive Guide
2.1 π¦ Install Dependencies
npm init -ynpm install express socket.io
2.2 Set Up a Socket.IO Server
index.js
const express = require('express');const http = require('http');const { Server } = require('socket.io');
const app = express();const server = http.createServer(app); // Create a raw HTTP serverconst io = new Server(server); // Bind Socket.IO to it
app.get('/', (req, res) => { res.sendFile(__dirname + '/index.html');});
// Handle socket connectionio.on('connection', (socket) => { console.log('A user connected');
socket.on('chat message', (msg) => { console.log('Message: ' + msg); io.emit('chat message', msg); // broadcast to all clients });
socket.on('disconnect', () => { console.log('A user disconnected'); });});
server.listen(3000, () => { console.log('Server running at http://localhost:3000');});
2.3 Client HTML (for testing)
index.html
<!DOCTYPE html><html> <head> <title>Socket.IO Chat</title> </head> <body> <ul id="messages"></ul> <form id="form" autocomplete="off"> <input id="input" /><button>Send</button> </form>
<script src="/socket.io/socket.io.js"></script> <script> const socket = io(); // Connect to server
const form = document.getElementById('form'); const input = document.getElementById('input'); const messages = document.getElementById('messages');
form.addEventListener('submit', (e) => { e.preventDefault(); if (input.value) { socket.emit('chat message', input.value); input.value = ''; } });
socket.on('chat message', function(msg) { const item = document.createElement('li'); item.textContent = msg; messages.appendChild(item); window.scrollTo(0, document.body.scrollHeight); }); </script> </body></html>
When you donβt specify IP/port:
const socket = io();
- This tries to connect to the same host and port that served the HTML page.
- It uses the current browser URLβs origin (like
http://localhost:3000
), so it works fine if your Express app and Socket.IO are served from the same place.
When you do need to specify IP/port:
const socket = io('http://localhost:3000');
Use this only if:
- The client and server are on different origins (e.g., server is hosted separately).
- Youβre serving the frontend separately (e.g., from a different React/Vite dev server).
β Summary:
Situation | io() is enough? |
---|---|
Frontend served by Express (same port) | β Yes |
Frontend on different port or domain | β No β use io('http://host:port') |
2.4 π‘ Core Socket.IO Concepts
1. π€ Emit and Receive Events
socket.emit('event-name', data); // to one clientio.emit('event-name', data); // to all clientssocket.broadcast.emit('event-name', data); // to everyone except sender (sender means the publisher who publish the messages to the topic/event-name)
2. π‘ Namespaces
Used to create multiple communication channels over one connection.
const chat = io.of('/chat');chat.on('connection', socket => { ... });
3. π Rooms
Used for grouping sockets (e.g., private rooms in chat).
socket.join('room1');io.to('room1').emit('message', 'hello room1');
4. π Handling Disconnections, Errors, and Reconnects
socket.on('disconnect', reason => { console.log('Disconnected:', reason);});
socket.on('connect_error', err => { console.error('Connection Error:', err);});
5. π Broadcasting to Others
socket.broadcast.emit('msg', 'User joined'); // exclude sender (publisher)
5.1 How Socket.IO actually works under the hood?
When you use socket.broadcast.emit(...)
, Socket.IO internally keeps a list of all active sockets, and it just sends the event to all of them except the one represented by socket
(i.e., the current connection that triggered the event).
π§ How Socket.IO knows which sockets to send to:
-
All connected sockets are stored in memory
-
Internally, Socket.IO maintains a map of all active sockets in the server using something like:
io.sockets.sockets // a Map of socket.id -> socket
-
-
When you call:
socket.broadcast.emit('chat message', msg);Socket.IO does this:
- Iterates over all connected sockets
- Skips the current
socket
- Emits
'chat message'
to everyone else
π§ͺ Hereβs a way to see it yourself:
io.on('connection', (socket) => { console.log('Socket connected:', socket.id);
socket.on('chat message', (msg) => { console.log('Sender:', socket.id); console.log('All sockets:', [...io.sockets.sockets.keys()]);
socket.broadcast.emit('chat message', msg); });});
This will print something like:
Sender: HJks8Zx7G1All sockets: [ 'HJks8Zx7G1', 'IUd7Hs9Wd2', 'AksP02HeqX' ]
So Socket.IO knows: βI should send this to IUd7Hs9Wd2
and AksP02HeqX
, but not HJks8Zx7G1
.β
5.2 What if youβre using rooms?
- Socket.IO supports rooms and namespaces to control who gets a message.
- Even in that case,
socket.broadcast.to(room).emit(...)
will only broadcast to the room excluding the sender.
5.2 β Summary:
socket.broadcast
is built into Socket.IO.- It uses the internal list of all sockets.
- It automatically skips the current one (
socket
). - No need for you to track who else is connected β unless you want finer control (e.g., by user ID).
6. π Authentication (Basic)
You can send auth info when connecting:
// clientconst socket = io({ auth: { token: "123" }});
// serverio.use((socket, next) => { const token = socket.handshake.auth.token; if (token === "123") next(); else next(new Error("unauthorized"));});
7. π¦ Bonus: Serve Static Files with Express
app.use(express.static('public')); // Place client files in 'public' directory
8. π§ͺ Testing
Use the browser console (console.log
), or tools like:
- Postman (for HTTP)
- Browser DevTools (for WS inspection under Network tab)
socket.io-client
in Node.js (for automated tests)
3. π§ What is a Namespace?
A namespace in Socket.IO lets you create separate communication channels on the same connection β useful for separating concerns (e.g., /chat
, /admin
).
π File: server.js
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);
// Default namespace ("/")io.on('connection', (socket) => { console.log('User connected to default namespace');});
// Chat namespaceconst chatNamespace = io.of('/chat');
chatNamespace.on('connection', (socket) => { console.log('User connected to /chat namespace');
socket.on('message', (msg) => { console.log('Chat message:', msg); chatNamespace.emit('message', msg); // Broadcast to all in /chat });
socket.on('disconnect', () => { console.log('User disconnected from /chat'); });});
server.listen(3000, () => { console.log('Server running at http://localhost:3000');});
π File: public/chat.html
<!DOCTYPE html><html> <head> <title>Chat Namespace</title> </head> <body> <h1>Chat</h1> <ul id="messages"></ul> <input id="input" /><button onclick="send()">Send</button>
<script src="/socket.io/socket.io.js"></script> <script> const socket = io('/chat'); // Connect to /chat namespace
socket.on('message', function (msg) { const li = document.createElement('li'); li.textContent = msg; document.getElementById('messages').appendChild(li); });
function send() { const input = document.getElementById('input'); socket.emit('message', input.value); input.value = ''; } </script> </body></html>
π Serve the client
Add this to server.js
to serve static files:
app.use(express.static('public'));
Make sure chat.html
is saved in a public
folder.
π Result:
- Client connects to
http://localhost:3000/chat.html
- Uses namespace
/chat
- Messages are isolated from other namespaces (like
/
or/admin
)