Skip to content

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

Terminal window
npm init -y
npm 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 server
const io = new Server(server); // Bind Socket.IO to it
app.get('/', (req, res) => {
res.sendFile(__dirname + '/index.html');
});
// Handle socket connection
io.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:
Situationio() 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

server.js
socket.emit('event-name', data); // to one client
io.emit('event-name', data); // to all clients
socket.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:
  1. 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
  2. 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: HJks8Zx7G1
All 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:

// client
const socket = io({
auth: {
token: "123"
}
});
// server
io.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 namespace
const 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)