Marcio Cunha

WebSockets: How to Build Real-Time Communication Between Server and Browser

Discover how WebSockets revolutionized the web by enabling bidirectional and instant communication between browsers and servers. Understand practical concepts, use cases, and real implementations.

Marcio Cunha12 min
Also available in:EspañolPortuguês
Summary
  • Traditional request-response architectures create unnecessary bottlenecks and latency for instant applications.
  • The WebSocket protocol establishes a persistent, bidirectional connection over a single TCP channel.
  • Exchanging messages via lightweight frames drastically reduces bandwidth consumption compared to repeated HTTP requests.
  • Properly managing connection life cycles and automatic reconnections ensures robustness in unstable network scenarios.
  • Chat systems, financial dashboards, and live tickers rely on this technology to deliver data without noticeable delays.

The Challenge of Instant Web Communication

During the early years of the internet, the standard operating model was simple and straightforward: the browser asked the server for information and waited for the response. This pattern, known as request and response, works perfectly for loading static pages, articles, and images. In practice, this means the page only knows what is happening if it actively asks the server whether there is any news. For news sites or social networks, developers created a technique called polling, where the browser asks the server every few seconds if there are new messages.

The major flaw in this approach is the massive waste of computing and network resources. Imagine hundreds of users asking 'is there anything new?' every five seconds, even when absolutely nothing has changed. The server spends most of its time responding that 'there is no news,' consuming processing power and generating useless network traffic. Furthermore, messages suffer noticeable delays, as the user only finds out about something new in the next round of questions. To solve this bottleneck in modern applications requiring immediate responses, software engineering needed a new communication paradigm that inverted this control logic.

Understanding WebSockets and the Persistent Channel

WebSockets emerged precisely to fill this gap, offering a bidirectional, full-duplex communication channel over a single TCP connection, which is the transmission control protocol responsible for ensuring data packets arrive intact from one point to another on the network. In simple terms, think of traditional polling as exchanging postcards where you must send a new postcard every time you want to know if you received a reply. The WebSocket, on the other hand, is equivalent to a phone call: once the call is established, both sides can speak and listen at the same time without hanging up and redialing.

Everything starts with an initial handshake that uses the good old HTTP protocol to negotiate the transition. The browser sends a special header telling the server it wants to upgrade the connection to the WebSocket standard. If the server accepts, the connection is upgraded and the protocol shifts from HTTP to WS, remaining open indefinitely. In practice, this means both the server and the client can send data at any time without the overhead of new request headers. This persistence eliminates the overhead of renegotiating connections with every message, ensuring the extreme speed required for online games, chats, and real-time financial dashboards.

Implementing a WebSocket Server and Client in Practice

To see this technology in action, we can examine a basic implementation using Node.js on the server side and vanilla JavaScript in the browser. The following code demonstrates how to create a server capable of listening for connections and responding to clients instantly. On the server side, we use a popular library called ws to manage connection lifecycles efficiently. The server runs on a specific port, waiting for clients to knock on the door to start the conversation.

const { WebSocketServer } = require('ws');

const wss = new WebSocketServer({ port: 8080 });

wss.on('connection', (ws) => {
  console.log('New client connected.');

  ws.on('message', (data) => {
    console.log(`Message received: ${data}`);
    ws.send(`Server echoes: ${data}`);
  });

  ws.on('close', () => {
    console.log('Client disconnected.');
  });
});

console.log('WebSocket server running on port 8080...');

In the code above, the connection event triggers whenever a new browser successfully establishes a connection. The message event listens for packets sent by the client, allowing the server to process incoming data and send an immediate response via the ws.send method. On the client side, the browser code is equally straightforward, requiring only a few lines to open the communication channel and start exchanging text messages or structured data in JSON format.

const socket = new WebSocket('ws://localhost:8080');

socket.onopen = () => {
  console.log('Connection established successfully.');
  socket.send('Hello, server!');
};

socket.onmessage = (event) => {
  console.log(`Server message: ${event.data}`);
};

socket.onclose = () => {
  console.log('Connection closed.');
};

Managing Network Challenges, Reconnection, and Scalability

Despite all its technical elegance, maintaining persistent connections brings considerable operational challenges to server infrastructure. In practice, mobile networks suffer frequent signal drops, computers enter hibernation mode, and Wi-Fi routers drop idle connections after a while. This means relying on an eternal connection without error handling is a surefire recipe for poor user experiences. To bypass this problem, developers implement heartbeat strategies, which are periodic pings sent to verify if the connection is still alive, combined with automatic reconnection algorithms featuring exponential backoff if the signal drops.

Another critical point is the horizontal scalability of servers handling WebSockets. When a traditional server relies on HTTP, any server instance behind a load balancer can handle a request. With WebSockets, the connection is tied to that specific machine because the TCP channel is open directly with it. If thousands of users connect, a single machine will exhaust its memory and file descriptor capacity. The solution to this dilemma involves using distributed message brokers, like Redis Pub/Sub, allowing different servers to exchange messages with each other and reach clients connected to separate instances of the application.

Final Thoughts on Using WebSockets

The introduction of WebSockets transformed the web from a static document environment into a dynamic platform for instant communication. Understanding when and how to use this technology is a game-changer for software engineers looking to build responsive and efficient systems. Although they require extra care with infrastructure, state management, and network resilience, the benefits far outweigh the complexity when the core requirement is immediate data delivery. By mastering the fundamental concepts of persistent connections, you gain the ability to design modern architectures capable of supporting high-performance real-time interactions.