Lesson 14-Node WebSocket Applications

Implementing Real-Time Communication

Installing a WebSocket Library

The most commonly used WebSocket library is ws. Install it via npm:

npm install ws

Creating a WebSocket Server

Basic WebSocket Server

const WebSocket = require('ws');

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

wss.on('connection', (ws) => {
  ws.on('message', (message) => {
    console.log(`Received: ${message}`);
  });

  ws.send('Hello Client!');
});

This code creates a WebSocket server listening on port 8080. When a new connection is established, the server sends a welcome message and logs any messages received from the client.

Handling Connections and Messages

Connection Event

The connection event is triggered when a new WebSocket connection is established. You can send initial data to the newly connected client in this event handler.

Message Event

The message event is triggered when a message is received from a client. In this handler, you can process the message and decide whether to broadcast it to other clients.

Broadcasting Messages

In a multi-user environment, you may need to broadcast messages to all connected clients. This can be achieved by iterating over the wss.clients collection and calling each client’s send method.

wss.broadcast = function broadcast(data) {
  wss.clients.forEach(function each(client) {
    if (client.readyState === WebSocket.OPEN) {
      client.send(data);
    }
  });
};

Implementing Client-Server Interaction

Client-Side JavaScript Code

On the client side, use the browser’s built-in WebSocket object to connect to the WebSocket server.

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

  socket.addEventListener('open', (event) => {
    socket.send('Hello Server!');
  });

  socket.addEventListener('message', (event) => {
    console.log(`Received: ${event.data}`);
  });
</script>

Handling Client Disconnections

When a client closes the WebSocket connection, the server triggers the close event. You can clean up resources or notify other clients in this handler.

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

Secure WebSocket (WSS)

For enhanced security, use TLS/SSL to encrypt WebSocket connections. This requires creating an HTTPS server with the https module and binding the WebSocket server to it.

const https = require('https');
const fs = require('fs');

const server = https.createServer({
  cert: fs.readFileSync('/path/to/cert.pem'),
  key: fs.readFileSync('/path/to/key.pem'),
});

const wss = new WebSocket.Server({ server });

server.listen(8080);

Coexisting WebSocket and HTTP Servers

In real-world applications, you may need to run a WebSocket server alongside an existing HTTP server. This can be done by binding the WebSocket server to the HTTP server.

const http = require('http');

const server = http.createServer((req, res) => {
  // Your HTTP request handling logic
});
const wss = new WebSocket.Server({ server });
server.listen(8080);

Heartbeat Detection and Error Handling

Heartbeat Detection

WebSocket connections may be terminated by network devices (e.g., routers, firewalls) during prolonged inactivity. To maintain connection validity, send periodic heartbeat packets.

const HEARTBEAT_INTERVAL = 20000;

wss.on('connection', (ws) => {
  let heartbeatTimeout;

  function sendHeartbeat() {
    heartbeatTimeout = setTimeout(() => {
      if (ws.readyState === WebSocket.OPEN) {
        ws.send('ping');
      }
      sendHeartbeat();
    }, HEARTBEAT_INTERVAL);
  }

  ws.on('pong', () => {
    clearTimeout(heartbeatTimeout);
    sendHeartbeat();
  });

  ws.on('close', () => {
    clearTimeout(heartbeatTimeout);
  });

  sendHeartbeat();
});

Error Handling

WebSocket connections may encounter errors like network interruptions or server crashes. Handle these in the error event handler to prevent crashes.

wss.on('connection', (ws) => {
  ws.on('error', (error) => {
    console.error('WebSocket error:', error);
  });
});

Multiplexing

In some cases, you may need to transmit multiple types of data over a single WebSocket connection. This can be achieved using a multiplexing protocol like Multiplex.

Installing Multiplex

npm install multiplex

Using Multiplex

const multiplex = require('multiplex');

const mplex = multiplex();

wss.on('connection', (ws) => {
  const mplexStream = mplex(ws);

  mplexStream.on('stream', (stream) => {
    stream.setEncoding('utf8');

    stream.on('data', (data) => {
      console.log('Received data:', data);
    });

    stream.pipe(stream);
  });
});

Real-Time Database Synchronization with WebSocket

In real-time applications, you may need to push database changes to clients instantly. This can be done by listening to database change events and sending updates via WebSocket.

Using Prisma to Listen for Database Changes

const { PrismaClient } = require('@prisma/client');
const prisma = new PrismaClient();

wss.on('connection', (ws) => {
  prisma.$on('postUpdate', (event) => {
    ws.send(JSON.stringify(event));
  });
});

Integrating WebSocket with Frontend Frameworks

When building real-time applications with frameworks like React, Vue, or Angular, use corresponding WebSocket libraries to simplify interactions with the WebSocket server.

Using React-WebSocket

npm install react-websocket

Using Vue-WebSocket

npm install vue-websocket

Using Angular WebSocket

npm install @aspnet/webapi-websockets

Using ws or socket.io Libraries

Using the ws Library

Installing ws

npm install ws

Creating a WebSocket Server

const WebSocket = require('ws');

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

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

  ws.on('message', (message) => {
    console.log(`Received: ${message}`);
    ws.send(`Echo: ${message}`);
  });

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

Creating a WebSocket Client

const WebSocket = require('ws');

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

ws.on('open', () => {
  console.log('Connected to server');
  ws.send('Hello Server!');
});

ws.on('message', (message) => {
  console.log(`Received: ${message}`);
});

ws.on('close', () => {
  console.log('Disconnected from server');
});

Using the socket.io Library

Installing socket.io

npm install socket.io

Creating a Socket.IO Server

const io = require('socket.io')(8080);

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

  socket.on('message', (message) => {
    console.log(`Received: ${message}`);
    socket.emit('message', `Echo: ${message}`);
  });

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

Creating a Socket.IO Client

<script src="/socket.io/socket.io.js"></script>
<script>
  const socket = io('http://localhost:8080');

  socket.on('connect', () => {
    console.log('Connected to server');
    socket.emit('message', 'Hello Server!');
  });

  socket.on('message', (message) => {
    console.log(`Received: ${message}`);
  });

  socket.on('disconnect', () => {
    console.log('Disconnected from server');
  });
</script>

Comparison of ws and socket.io

  • Performance: ws is closer to the low-level protocol, offering higher performance; socket.io includes additional features and compatibility handling.
  • Features: socket.io provides more features like automatic reconnection, heartbeat detection, and cross-origin support.
  • Ease of Use: socket.io is more user-friendly with rich events and APIs; ws is better for highly customized applications.

Practical Case Study: Real-Time Chat Application

Creating a WebSocket Server

const WebSocket = require('ws');

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

wss.on('connection', (ws) => {
  ws.on('message', (message) => {
    wss.clients.forEach((client) => {
      if (client !== ws && client.readyState === WebSocket.OPEN) {
        client.send(message);
      }
    });
  });
});

Creating a Socket.IO Server

const io = require('socket.io')(8080);

io.on('connection', (socket) => {
  socket.on('chat message', (msg) => {
    io.emit('chat message', msg);
  });
});

Implementing a Custom Protocol

Beyond standard text and binary data, you can define custom protocols to transmit structured data, such as a JSON-based protocol with command and parameter fields for more flexible communication.

// Server side using ws
const WebSocket = require('ws');
const wss = new WebSocket.Server({ port: 8080 });

wss.on('connection', (ws) => {
  ws.on('message', (message) => {
    const data = JSON.parse(message);
    if (data.command === 'subscribe') {
      // Handle subscription
    }
    // More command handlers...
  });
});

// Client side using ws
const WebSocket = require('ws');
const ws = new WebSocket('ws://localhost:8080');

ws.on('open', () => {
  ws.send(JSON.stringify({ command: 'subscribe', topic: 'news' }));
});

Real-Time Database Synchronization with WebSocket

Combine WebSocket with database real-time updates to push changes to clients instantly, using features like PostgreSQL’s LISTEN/NOTIFY or MongoDB’s Change Streams.

// PostgreSQL example with pg module
const { Pool } = require('pg');
const pool = new Pool({
  user: 'postgres',
  host: 'localhost',
  database: 'testdb',
  password: 'password',
  port: 5432,
});

pool.query('LISTEN channel_name;').then(() => {
  process.stdin.resume(); // Keep process alive
});

process.on('message', (payload) => {
  if (payload.channel === 'channel_name') {
    // Send notification to WebSocket clients
  }
});

WebSocket in a Microservices Architecture

In a microservices architecture, services can use WebSocket for real-time communication, enabling data sharing and collaboration, such as transmitting logs or monitoring data.

// Service A
const WebSocket = require('ws');
const wss = new WebSocket.Server({ port: 8081 });

wss.on('connection', (ws) => {
  ws.on('message', (message) => {
    // Process message and respond
  });
});

// Service B
const WebSocket = require('ws');
const ws = new WebSocket('ws://service-a:8081');

ws.on('open', () => {
  ws.send('Request data');
});

ws.on('message', (message) => {
  // Process received data
});

Summary

Both ws and socket.io are powerful tools for implementing WebSocket functionality. ws is ideal for performance-critical scenarios, while socket.io excels in functionality and ease of use. Depending on your application needs, choose the appropriate library to implement WebSocket communication. Whether for simple real-time data pushing or complex multiplayer interactions, WebSocket technology provides efficient and reliable solutions.

Share your love