Real-time communication with WebSockets and Socket.IO in Node.js

Sushant Gaurav - Oct 2 - - Dev Community

Modern web applications often require real-time communication, whether for chat systems, live updates, collaborative editing, or notifications. Traditional HTTP communication is insufficient for real-time applications as it relies on request-response patterns. This is where WebSockets come into play, allowing for full-duplex communication between the server and the client.

In this article, we will explore:

  1. What WebSockets are, and why they are essential for real-time applications.
  2. How to implement WebSockets in Node.js.
  3. Using Socket.IO to simplify WebSocket implementation.
  4. Examples of real-world use cases.

What Are WebSockets?

WebSockets are a protocol that provides full-duplex communication channels over a single TCP connection. Unlike traditional HTTP requests, which are request-response-based and have overhead in continuously establishing new connections, WebSockets allow persistent communication.

Key Features:

  • Full-Duplex Communication: Both client and server can send messages at any time without waiting for each other.
  • Lower Latency: WebSockets eliminate the need to constantly open and close connections, reducing latency and improving performance for real-time apps.
  • Event-Driven: Both sides can push data, making it ideal for dynamic applications like chat, gaming, or real-time notifications.

Implementing WebSockets in Node.js

Step 1: Installing the ws WebSocket Library

In Node.js, WebSockets are not built-in, so we need a library. One of the most popular WebSocket libraries for Node.js is ws.

npm install ws
Enter fullscreen mode Exit fullscreen mode

Step 2: Creating a WebSocket Server

Here’s a simple WebSocket server using the ws library:

const WebSocket = require('ws');

const wss = new WebSocket.Server({ port: 8080 });

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

    // Listen for messages from the client
    ws.on('message', (message) => {
        console.log('Received:', message);
        // Respond to the client
        ws.send(`Hello, you sent -> ${message}`);
    });

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

console.log('WebSocket server running on ws://localhost:8080');
Enter fullscreen mode Exit fullscreen mode

In this example:

  • We create a WebSocket server on port 8080.
  • When a client connects, the connection event triggers.
  • The server listens for messages from the client and responds with a confirmation message.
  • If the client disconnects, we log a message.

Step 3: Creating a WebSocket Client

You can create a WebSocket client using JavaScript in the browser:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>WebSocket Client</title>
</head>
<body>
    <h1>WebSocket Example</h1>
    <button id="sendBtn">Send Message</button>
    <script>
        const ws = new WebSocket('ws://localhost:8080');

        ws.onopen = () => {
            console.log('Connected to WebSocket server');
        };

        ws.onmessage = (event) => {
            console.log('Message from server:', event.data);
        };

        document.getElementById('sendBtn').onclick = () => {
            ws.send('Hello from client');
        };
    </script>
</body>
</html>
Enter fullscreen mode Exit fullscreen mode
  • When the WebSocket connection opens, the client logs a success message.
  • A message is sent to the server when the button is clicked, and the server’s response is displayed.

Output:

  1. Client logs:
   Connected to WebSocket server
   Message from server: Hello, you sent -> Hello from client
Enter fullscreen mode Exit fullscreen mode
  1. Server logs:
   Client connected
   Received: Hello from client
Enter fullscreen mode Exit fullscreen mode

Socket.IO: Simplified WebSockets

While WebSockets are powerful, they require some manual work for handling events like reconnections, broadcasting, and managing fallbacks (e.g., long polling). Socket.IO simplifies real-time communication by providing features like:

  • Automatic reconnection.
  • Broadcasting events to multiple clients.
  • Fallback to polling if WebSockets are not supported.

Step 1: Installing Socket.IO

npm install socket.io
Enter fullscreen mode Exit fullscreen mode

Step 2: Creating a Socket.IO Server

const http = require('http');
const socketio = require('socket.io');

// Create an HTTP server
const server = http.createServer();
const io = socketio(server);

io.on('connection', (socket) => {
    console.log('Client connected');

    // Listen for messages from the client
    socket.on('message', (msg) => {
        console.log('Received message:', msg);
        socket.emit('message', `Server received: ${msg}`);
    });

    // On client disconnect
    socket.on('disconnect', () => {
        console.log('Client disconnected');
    });
});

server.listen(3000, () => {
    console.log('Socket.IO server running on http://localhost:3000');
});
Enter fullscreen mode Exit fullscreen mode

Here:

  • We create a server using Socket.IO that listens for connections and incoming messages.
  • socket.emit is used to send a message back to the client.

Step 3: Creating a Socket.IO Client

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Socket.IO Client</title>
</head>
<body>
    <h1>Socket.IO Example</h1>
    <button id="sendBtn">Send Message</button>
    <script src="/socket.io/socket.io.js"></script>
    <script>
        const socket = io('http://localhost:3000');

        socket.on('connect', () => {
            console.log('Connected to server');
        });

        socket.on('message', (msg) => {
            console.log('Message from server:', msg);
        });

        document.getElementById('sendBtn').onclick = () => {
            socket.send('Hello from client');
        };
    </script>
</body>
</html>
Enter fullscreen mode Exit fullscreen mode

In this example, the Socket.IO client automatically handles the connection to the server and allows bidirectional communication.

Output:

  1. Client logs:
   Connected to server
   Message from server: Server received: Hello from client
Enter fullscreen mode Exit fullscreen mode
  1. Server logs:
   Client connected
   Received message: Hello from client
Enter fullscreen mode Exit fullscreen mode

Use Cases for WebSockets and Socket.IO

4.1 Real-Time Chat Application

Problem: A messaging platform needs to enable real-time communication between users.

Solution: WebSockets (or Socket.IO) provide an event-driven, persistent connection, allowing users to send and receive messages in real time without delays.

4.2 Real-Time Collaborative Document Editing

Problem: Users collaborating on a document need to see changes made by others instantly.

Solution: By using WebSockets, changes are pushed to all clients in real time, ensuring that the document remains synchronized across all users.

4.3 Notifications in Web Applications

Problem: A web application needs to notify users of events like new messages, updates, or alerts.

Solution: WebSockets allow the server to push notifications to the client as soon as events occur, improving user engagement.

Scaling WebSockets and Socket.IO

Scaling WebSockets and Socket.IO is challenging because of their persistent nature. Each WebSocket connection is a long-lived TCP connection, which can become resource-intensive as the number of connections increases.

Common Strategies for Scaling:

  1. Horizontal Scaling: Load balancing WebSocket traffic across multiple servers.
  2. Sticky Sessions: Ensuring that all WebSocket connections from a client are routed to the same server.
  3. Redis for Pub/Sub: Using Redis for event publishing and subscription across servers, ensuring all clients receive real-time updates.
npm install socket.io-redis
Enter fullscreen mode Exit fullscreen mode

Here’s an example of configuring Socket.IO to use Redis:

const io = require('socket.io')(server);
const redisAdapter = require('socket.io-redis');
io.adapter(redisAdapter({ host: 'localhost', port: 6379 }));
Enter fullscreen mode Exit fullscreen mode

Conclusion

WebSockets and Socket.IO are essential technologies for real-time communication in modern web applications. While WebSockets provide a low-level protocol for full-duplex communication, Socket.IO simplifies the implementation, offering additional features like automatic reconnection and broadcasting. Both technologies have significant real-world applications, from chat applications to collaborative tools.

In the next article, we’ll dive into Node.js Streams and explore how they handle large data efficiently, focusing on scalability and performance.

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .