What Is a WebSocket Server?
A WebSocket server is a long-lived TCP endpoint that, after an initial HTTP handshake, keeps a full-duplex connection open so the server can push data to a client at any time without the client re-requesting it. In Node.js this is typically built with the ws package on top of the built-in http module, since Node's non-blocking event loop is well suited to holding thousands of concurrent sockets open cheaply.
Cricket analogy: Like a stadium PA system that stays live through the whole innings rather than a scoreboard you must refresh after every over, a WebSocket keeps broadcasting Kohli's runs the instant they happen instead of waiting to be asked.
The Handshake and the `ws` Library
Every WebSocket connection begins as a normal HTTP GET request carrying an Upgrade: websocket header and a Sec-WebSocket-Key; the server responds with HTTP status 101 and a computed Sec-WebSocket-Accept value, after which the TCP socket switches from HTTP framing to the WebSocket binary frame protocol. The ws npm package handles this handshake for you: attaching a WebSocketServer to an existing http.Server lets you share a single port between REST routes and socket upgrades, which is the standard production pattern.
Cricket analogy: It's like the toss before a match — a short, formal exchange (captain calls heads, umpire confirms) that switches the day from pre-match formality into the actual continuous play of the innings.
const http = require('http');
const { WebSocketServer } = require('ws');
const server = http.createServer((req, res) => {
res.writeHead(200);
res.end('HTTP endpoint still works alongside the socket server');
});
const wss = new WebSocketServer({ server, path: '/ws' });
wss.on('connection', (socket, request) => {
console.log('Client connected from', request.socket.remoteAddress);
socket.on('message', (data) => {
// Broadcast the message to every other connected client
wss.clients.forEach((client) => {
if (client !== socket && client.readyState === client.OPEN) {
client.send(data.toString());
}
});
});
socket.on('close', () => console.log('Client disconnected'));
});
server.listen(3000, () => console.log('Listening on :3000'));
Handling Connections, Messages, and Broadcasts
Each accepted connection fires a connection event with a WebSocket instance representing that specific client; from there you attach message, close, and error listeners on the individual socket rather than on the server. Broadcasting to all clients means iterating wss.clients and checking readyState === WebSocket.OPEN before calling send(), since a socket can be mid-close when a broadcast fires and sending to a closed socket throws.
Cricket analogy: It's like a fielding captain giving individual instructions to each fielder by name over the radio mic rather than shouting one blanket command, and checking each fielder is actually on the field before relaying it.
ws also supports per-message-deflate compression and a perMessageDeflate option; enabling it can cut bandwidth significantly for chatty JSON payloads but adds CPU overhead per message, so benchmark before turning it on for high-throughput servers.
Scaling and Production Considerations
A single Node.js process holding open sockets does not automatically share state across multiple instances, so horizontally scaled deployments need a pub/sub backplane such as Redis to fan messages out across processes, plus sticky sessions or a shared session store so a reconnecting client lands wherever its state lives. Health checks, heartbeats via ping/pong frames, and idle-timeout logic are also essential in production because TCP alone won't reliably detect a half-open connection left behind by a client that lost network without closing cleanly.
Cricket analogy: It's like the IPL running matches simultaneously in Mumbai and Chennai but still needing a shared central scoring feed so both broadcasts and the official website stay in sync, rather than each stadium keeping its own disconnected scorebook.
Never trust readyState alone to mean the client is actually reachable — a socket can stay OPEN for minutes after the underlying network died. Implement ping/pong heartbeats and terminate sockets that miss several consecutive pongs, or you'll leak memory holding zombie connections.
- A WebSocket connection starts as an HTTP request with an
Upgradeheader and switches to persistent, full-duplex framing after a 101 response. - Node's
wspackage attaches aWebSocketServerto an existinghttp.Server, letting REST and socket traffic share one port. - Each client connection is its own
WebSocketobject; listen formessage,close, anderrorper socket, not globally. - Broadcasting means iterating
wss.clientsand checkingreadyState === OPENbefore callingsend(). - Horizontal scaling requires an external pub/sub layer like Redis so messages fan out across multiple Node processes.
- Heartbeats (
ping/pong) are necessary because TCP alone won't detect a client that disappeared without closing cleanly. perMessageDeflatecompression trades CPU for bandwidth and should be benchmarked before enabling in high-throughput servers.
Practice what you learned
1. What HTTP status code confirms a successful WebSocket handshake?
2. In the `ws` library, how do you avoid sending to a client that is mid-close during a broadcast?
3. Why do horizontally scaled WebSocket deployments typically need Redis or a similar pub/sub layer?
4. What is the primary purpose of ping/pong frames?
5. What does enabling perMessageDeflate primarily trade off?
Was this page helpful?
You May Also Like
WebSockets in the Browser
A practical guide to the native browser WebSocket API — connecting, sending and receiving messages, handling reconnection, and working with binary data efficiently.
Socket.IO Explained
Understand what Socket.IO adds on top of raw WebSockets — rooms, namespaces, automatic reconnection, and transport fallback — and when it's the wrong tool for the job.
WebSockets with Python
Build asynchronous WebSocket servers and clients in Python using the `websockets` library and asyncio, and integrate real-time endpoints into a FastAPI app.
Authenticating WebSocket Connections
Learn how to securely authenticate long-lived WebSocket connections, including handshake-time token validation, per-message authorization, and common security pitfalls.
Related Reading
Related Study Notes in Web Development
Browse all study notesWebAssembly Study Notes
WebAssembly · 30 topics
Web DevelopmentgRPC Study Notes
Protocol Buffers · 30 topics
Web DevelopmentSpring Boot Study Notes
Java · 30 topics
Web DevelopmentFlask Study Notes
Python · 30 topics
Web DevelopmentDjango Study Notes
Python · 30 topics
Web DevelopmentNext.js Study Notes
JavaScript · 30 topics