Skip to main content

Understanding WebSockets: A Comprehensive Guide

· 5 min read
PSVNL Sai Kumar
Senior Software Development Engineer, Oracle

WebSockets enable real-time, full-duplex communication between a client and server over a single persistent TCP connection. Unlike HTTP, where the client initiates every request, WebSockets allow the server to push data to the client at any time — no polling required.

WebSockets vs. HTTP: The Core Difference

HTTP is a request-response protocol. Every interaction requires the client to open a connection, send a request, wait for a response, and close the connection. This works for static page loads but is inefficient for real-time features.

HTTP (REST)WebSocket
ConnectionNew connection per requestSingle persistent connection
DirectionClient → Server onlyBidirectional
LatencyNew TCP + TLS handshake each timeEstablished once
OverheadFull HTTP headers every requestMinimal framing after handshake
Server pushNot supported (without SSE/polling)Native
Best forCRUD APIs, document retrievalChat, live feeds, multiplayer, IoT

How WebSockets Work

Step 1: The Handshake

A WebSocket connection starts as an HTTP request. The client sends an Upgrade header:

GET /ws HTTP/1.1
Host: example.com
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==
Sec-WebSocket-Version: 13

Step 2: Server Accepts

If the server supports WebSockets, it responds with HTTP 101:

HTTP/1.1 101 Switching Protocols
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=

From this point, the TCP connection is no longer HTTP. Both sides can send frames at any time.

Step 3: Framing

WebSocket messages are sent as frames. A frame contains:

  • An opcode (text, binary, ping, pong, close)
  • A payload length
  • An optional masking key (clients must mask frames; servers must not)
  • The payload data

This framing is lightweight: a small text frame adds only 2–6 bytes of overhead.

Step 4: Closure

Either side can send a close frame. The receiving side must respond with a close frame, then both sides tear down the TCP connection.

Use Cases

Real-time applications that need WebSockets:

  • Chat: Instant message delivery without polling
  • Collaborative editing: Google Docs-style concurrent document changes
  • Live dashboards: Stock tickers, system metrics, analytics in real time
  • Multiplayer games: Low-latency game state synchronization
  • Financial trading: Order book updates, price feeds
  • IoT: Sensor data streams, device control

Applications that do NOT need WebSockets:

  • Standard CRUD APIs — use REST or GraphQL
  • Infrequent updates (once per minute) — use HTTP polling or SSE
  • File uploads/downloads — standard HTTP handles these better

Server-Side Implementation

Node.js with ws

const WebSocket = require('ws');
const server = new WebSocket.Server({ port: 8080 });

server.on('connection', (ws) => {
console.log('Client connected');

ws.on('message', (message) => {
console.log('Received:', message.toString());
// Broadcast to all connected clients
server.clients.forEach((client) => {
if (client.readyState === WebSocket.OPEN) {
client.send(message.toString());
}
});
});

ws.on('close', () => console.log('Client disconnected'));
ws.send('Connected to server');
});

Browser Client

const socket = new WebSocket('wss://example.com/ws');

socket.addEventListener('open', () => {
console.log('Connected');
socket.send(JSON.stringify({ type: 'hello', text: 'Hi server!' }));
});

socket.addEventListener('message', (event) => {
const data = JSON.parse(event.data);
console.log('Message from server:', data);
});

socket.addEventListener('close', (event) => {
console.log(`Closed: code=${event.code}, reason=${event.reason}`);
// Reconnect logic here
});

socket.addEventListener('error', (err) => {
console.error('WebSocket error:', err);
});

Security

Always Use wss://

ws:// sends data in plaintext — equivalent to http://. Use wss:// (WebSocket Secure = WebSocket over TLS) in all production environments.

Validate the Origin Header

Browsers send an Origin header in the WebSocket handshake. If your server ignores it, any malicious website can open a WebSocket connection to your server using the user's session cookies (cross-site WebSocket hijacking).

Always check that the origin matches your known domains before upgrading the connection.

Authenticate Before Upgrading

The WebSocket handshake is an HTTP request — you can read cookies, query parameters, or Authorization headers:

// Option 1: token in query string
// wss://example.com/ws?token=abc123
const token = new URL(request.url, 'http://base').searchParams.get('token');

// Option 2: cookie-based (automatic in browsers)
const sessionCookie = parseCookie(request.headers.cookie);

Reject the upgrade (return HTTP 401) if authentication fails — once upgraded to WebSocket, you have less control over the connection.

Message Validation

Never trust incoming WebSocket messages. Validate message types, sizes, and content before processing — treat WebSocket input with the same caution as any user input.

Scaling WebSockets

A WebSocket connection holds a file descriptor on the server for its lifetime. A single Node.js process can typically handle ~50,000–100,000 concurrent connections before hitting limits.

Horizontal scaling with Redis Pub/Sub:

When you have multiple server instances, a message received by instance A needs to reach clients connected to instances B and C. Use a message broker:

Client A → Server Instance 1 → Redis Pub/Sub → Server Instance 2 → Client B

Each server instance subscribes to a Redis channel. When it receives a WebSocket message, it publishes to Redis. All instances receive the publication and forward it to their local connected clients.

WebSockets vs. Alternatives

WebSocketsServer-Sent Events (SSE)Long Polling
DirectionBidirectionalServer → Client onlyBidirectional
ProtocolCustom over TCPHTTPHTTP
Browser supportExcellentGood (no IE)Universal
Auto-reconnectManualBuilt-inManual
OverheadVery lowLowHigh (new request per message)
Best forChat, games, bidirectional feedsNews feeds, notificationsLegacy fallback

Common Pitfalls

  1. No reconnection logic: Networks are unreliable. Implement exponential backoff reconnection on the client.
  2. Storing connection state in memory only: If the server restarts, all connections drop. Persist important state to a database.
  3. Forgetting ping/pong: Idle connections can be silently dropped by proxies and NAT gateways. Implement heartbeats.
  4. Unbounded message queues: If a slow client can't consume messages fast enough, the queue grows without bound. Set limits and drop or disconnect lagging clients.