How WebSocket API Establishes Full-Duplex Connections
The WebSocket API establishes persistent, full-duplex communication channels over a single TCP connection by upgrading a standard HTTP request to the WebSocket protocol. Unlike the traditional request-response model of HTTP, WebSockets enable simultaneous, bi-directional data transfer between a client (such as a web browser) and a server with minimal latency and overhead. This article explains the underlying handshake process, the transition to persistent framing, and how to implement the native WebSocket interface in JavaScript.
The WebSocket Handshake (Protocol Upgrade)
The connection process begins with a standard HTTP/1.1 request initiated by the client, known as the WebSocket Handshake. This request asks the server to upgrade the connection protocol:
- Client Handshake Request: The browser sends an HTTP
GETrequest containing specific upgrade headers:Connection: Upgrade— Signals the intent to change the protocol.Upgrade: websocket— Specifies the target protocol.Sec-WebSocket-Key— A base64-encoded random key to verify that the server supports the WebSocket protocol.Sec-WebSocket-Version— Specifies the protocol version (usually13).
- Server Handshake Response: If the server supports
WebSockets, it accepts the request and responds with an HTTP status code
101 Switching Protocols. The response includes:Connection: UpgradeUpgrade: websocketSec-WebSocket-Accept— A hashed value generated using the client’sSec-WebSocket-Keycombined with a standard GUID, proving the server recognized the handshake.
Once this handshake completes, the HTTP layer is dropped, and the underlying TCP connection remains open for raw WebSocket traffic.
Maintaining the Full-Duplex Persistent Connection
After the upgrade, the connection switches from HTTP to the WebSocket
framing protocol (using the ws:// or encrypted
wss:// URI scheme):
- Bi-directional Framing: Data is split into lightweight frames (containing text, binary, or control data) with minimal header overhead (typically 2 to 10 bytes). Either the client or the server can send messages at any time without waiting for a request from the other party.
- Persistent State: The TCP socket stays open indefinitely until either the client, the server, or a network failure closes it.
- Heartbeats (Ping/Pong): Control frames
(
PingandPong) are exchanged periodically beneath the application layer to verify that the connection is still alive and to prevent intermediate network proxies or routers from closing idle sockets.
Implementing WebSockets in JavaScript
The browser provides a built-in WebSocket object that
abstracts the low-level TCP and handshake management into an
event-driven interface.
1. Establishing the Connection
Creating a new WebSocket instance immediately initiates
the HTTP upgrade handshake:
const socket = new WebSocket('wss://example.com/socket');2. Listening for Connection Events
The client monitors the connection state using event listeners:
// Triggered when the handshake succeeds and the connection is open
socket.addEventListener('open', (event) => {
console.log('Connected to WebSocket server.');
// Send initial data to the server
socket.send(JSON.stringify({ type: 'INIT', payload: 'Hello Server' }));
});
// Triggered whenever the server pushes data to the client
socket.addEventListener('message', (event) => {
console.log('Message from server:', event.data);
});
// Triggered when an error occurs
socket.addEventListener('error', (error) => {
console.error('WebSocket Error:', error);
});
// Triggered when the connection is closed by either party
socket.addEventListener('close', (event) => {
console.log(`Connection closed: ${event.code} - ${event.reason}`);
});3. Sending Data
Data can be sent immediately once the readyState is
WebSocket.OPEN:
function sendMessage(messageObject) {
if (socket.readyState === WebSocket.OPEN) {
socket.send(JSON.stringify(messageObject));
} else {
console.warn('Socket is not open. State:', socket.readyState);
}
}4. Closing the Connection
Either side can gracefully terminate the connection using the
close() method:
socket.close(1000, 'Work complete');Through this combination of the HTTP upgrade mechanism, lightweight data framing, and JavaScript’s event-driven API, WebSockets provide an efficient foundation for real-time applications such as live chats, multiplayer games, and financial dashboards.