WebSocket Protocol Internals
WebSocket is a persistent, full-duplex communication protocol over a single TCP connection. Unlike HTTP (request/response), WebSocket allows both server and client to send messages at any time. The connection starts as HTTP then upgrades via a handshake.
WebSocket vs HTTP vs SSE
- **HTTP/REST**: Request → Response. Client must initiate every exchange. Server cannot push data without being asked. Best for: CRUD, file downloads, most APIs
- **Server-Sent Events (SSE)**: One-way persistent stream from server to client. Client connects once, server pushes updates. Best for: notifications, live feeds, dashboards where client only needs to receive
- **WebSocket**: Full-duplex persistent connection. Both sides can send at any time. Best for: chat, multiplayer games, collaborative editing, live trading — anything requiring bidirectional real-time communication
- **Upgrade handshake**: Client sends HTTP GET with `Upgrade: websocket` header. Server responds with 101 Switching Protocols. TCP connection is now a WebSocket connection — HTTP is no longer used on this socket
- **Frames**: Data is sent as binary frames with opcodes: 0x1 (text), 0x2 (binary), 0x8 (close), 0x9 (ping), 0xA (pong). Socket.io adds its own framing on top for features like rooms and acknowledgements
Raw WebSocket Server (ws library)
import { WebSocketServer, WebSocket as WS } from 'ws';
import http from 'http';
import jwt from 'jsonwebtoken';
// npm install ws
// npm install -D @types/ws
const server = http.createServer();
const wss = new WebSocketServer({ server });
interface AuthenticatedSocket extends WS {
userId?: string;
isAlive?: boolean;
}
wss.on('connection', (ws: AuthenticatedSocket, req) => {
// Authenticate on connect via token in query string
const url = new URL(req.url!, 'ws://localhost');
const token = url.searchParams.get('token');
try {
const payload = jwt.verify(token ?? '', process.env.JWT_SECRET!) as { userId: string };
ws.userId = payload.userId;
ws.isAlive = true;
} catch {
ws.close(4001, 'Unauthorized'); // custom close code
return;
}
ws.on('message', (data: Buffer) => {
const message = JSON.parse(data.toString());
// Echo back to sender
ws.send(JSON.stringify({ type: 'echo', data: message }));
});
ws.on('pong', () => { ws.isAlive = true; }); // heartbeat response
ws.on('close', (code, reason) => {
console.log(`Client ${ws.userId} disconnected: ${code} ${reason}`);
});
});
// ━━ Heartbeat — detect dead connections ━━
// Clients that crash don't always send a close frame — detect with ping/pong
const heartbeat = setInterval(() => {
wss.clients.forEach((ws) => {
const socket = ws as AuthenticatedSocket;
if (!socket.isAlive) { socket.terminate(); return; }
socket.isAlive = false;
socket.ping(); // client must respond with pong
});
}, 30_000); // ping every 30s
wss.on('close', () => clearInterval(heartbeat));
server.listen(3000);Tip
Tip
Practice WebSocket Protocol Internals in small, isolated examples before integrating into larger projects. Breaking concepts into small experiments builds genuine understanding faster than reading alone.
Persistent bidirectional communication.
Practice Task
Note
Practice Task — (1) Write a working example of WebSocket Protocol Internals from scratch without looking at notes. (2) Modify it to handle an edge case (empty input, null value, or error state). (3) Share your solution in the Priygop community for feedback.
Quick Quiz
Common Mistake
Warning
A common mistake with WebSocket Protocol Internals is skipping edge case testing — empty inputs, null values, and unexpected data types. Always validate boundary conditions to write robust, production-ready node code.
Key Takeaways
- WebSocket is a persistent, full-duplex communication protocol over a single TCP connection.
- *HTTP/REST**: Request → Response. Client must initiate every exchange. Server cannot push data without being asked. Best for: CRUD, file downloads, most APIs
- *Server-Sent Events (SSE)**: One-way persistent stream from server to client. Client connects once, server pushes updates. Best for: notifications, live feeds, dashboards where client only needs to receive
- *WebSocket**: Full-duplex persistent connection. Both sides can send at any time. Best for: chat, multiplayer games, collaborative editing, live trading — anything requiring bidirectional real-time communication