JavaScript WebSockets Real-Time Communication

This article explores how JavaScript interacts with WebSockets to establish persistent, full-duplex communication channels between clients and servers. You will learn the core mechanics of the native WebSocket API, how event listeners handle the connection lifecycle and incoming data, and how to send and receive messages instantly without the overhead of traditional HTTP polling.

The WebSocket Protocol vs. HTTP

Traditional web communication relies on the HTTP request-response model, where the client must repeatedly initiate requests to retrieve new data (polling). WebSockets solve this limitation by initiating a standard HTTP handshake that “upgrades” to a persistent, bidirectional TCP connection over the ws:// (unencrypted) or wss:// (TLS encrypted) protocol. Once established, both the client and server can send messages independently at any time with minimal latency and packet overhead.

Initializing a WebSocket Connection in JavaScript

Browsers provide the native WebSocket interface, eliminating the need for external libraries for standard implementations. To initiate a connection, create a new instance of the WebSocket object by passing the target server URL:

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

Handling WebSocket Events

JavaScript manages WebSocket interactions asynchronously through four primary event handlers:

1. Connection Open (open)

The open event fires when the connection is successfully established and ready to transmit data.

socket.addEventListener('open', (event) => {
    console.log('Connected to WebSocket server.');
    socket.send(JSON.stringify({ type: 'INIT', payload: 'Client connected' }));
});

2. Receiving Messages (message)

The message event triggers whenever the server pushes data to the client. The received payload is accessible via the data property of the event object, which can be plain text, JSON, or binary data (such as Blob or ArrayBuffer).

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

3. Error Handling (error)

The error event triggers when an unexpected issue interrupts communication, such as a network failure or invalid handshake.

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

4. Connection Close (close)

The close event fires when the connection terminates, either cleanly by either party or abruptly due to network disconnection.

socket.addEventListener('close', (event) => {
    console.log(`Disconnected. Code: ${event.code}, Reason: ${event.reason}`);
});

Sending Data to the Server

To send data to the server, use the socket.send() method. The method accepts strings, Blobs, ArrayBuffers, or TypedArrays. Structured data should typically be serialized to a JSON string before sending:

function sendMessage(type, text) {
    if (socket.readyState === WebSocket.OPEN) {
        const payload = JSON.stringify({ type, text, timestamp: Date.now() });
        socket.send(payload);
    } else {
        console.warn('WebSocket is not open. State:', socket.readyState);
    }
}

Connection Ready States

Before sending data, you can check the socket.readyState property, which returns one of four numeric constants:

Terminating the Connection

To close the connection programmatically from the client, call the close() method, optionally passing a numeric status code and a human-readable reason string:

socket.close(1000, 'Normal closure by client');