Module: Browser APIs

Web Sockets

JavaScript Essentials: Browser APIs - WebSockets

WebSockets provide a full-duplex communication channel over a single TCP connection. This allows for real-time, bidirectional communication between a web browser and a server, unlike the traditional request-response model of HTTP. They are crucial for applications requiring instant updates, like chat applications, online games, live data feeds, and collaborative editing tools.

Why WebSockets?

  • Real-time Communication: Unlike HTTP polling (repeatedly asking the server for updates), WebSockets maintain a persistent connection, allowing the server to push data to the client as soon as it's available.
  • Full-Duplex: Data can flow in both directions simultaneously.
  • Reduced Overhead: After the initial handshake, data frames are smaller than HTTP headers, reducing bandwidth usage.
  • Efficiency: Avoids the overhead of repeatedly establishing and closing HTTP connections.

How WebSockets Work

  1. Handshake: The communication begins with an HTTP handshake. The client sends a WebSocket handshake request to the server.
  2. Connection Establishment: If the server supports WebSockets and accepts the handshake, it responds with a 101 Switching Protocols status code, upgrading the connection to the WebSocket protocol.
  3. Data Transfer: Once the connection is established, data is exchanged using WebSocket frames. These frames contain data and control information.
  4. Connection Closure: Either the client or the server can close the connection.

JavaScript WebSocket API

The WebSocket object in JavaScript provides the interface for working with WebSockets.

1. Creating a WebSocket Connection:

const socket = new WebSocket('ws://example.com:8080'); // Replace with your WebSocket server URL
  • ws:// is the protocol for unencrypted WebSocket connections.
  • wss:// is the protocol for encrypted WebSocket connections (recommended for production). Requires a server with SSL/TLS configured.

2. Event Handlers:

  • onopen: Triggered when the connection is successfully established.

    socket.onopen = (event) => {
      console.log('Connection opened!');
      socket.send('Hello, Server!'); // Send initial message
    };
    
  • onmessage: Triggered when a message is received from the server. The event object contains a data property with the message content.

    socket.onmessage = (event) => {
      console.log('Message from server:', event.data);
    };
    
  • onclose: Triggered when the connection is closed. The event object contains a code property indicating the closure reason and a reason property with a human-readable explanation.

    socket.onclose = (event) => {
      console.log('Connection closed:', event.code, event.reason);
    };
    
  • onerror: Triggered when an error occurs.

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

3. Sending Data:

socket.send('This is a message to the server.');
  • You can send text strings or ArrayBuffer objects (for binary data).

4. Closing the Connection:

socket.close(); // Closes the connection
socket.close(1000, 'Normal Closure'); // Closes with a specific code and reason
  • code: A numeric code indicating the reason for closure (e.g., 1000 for normal closure).
  • reason: A human-readable string explaining the closure.

5. WebSocket Properties:

  • readyState: An integer representing the state of the connection:
    • 0: CONNECTING
    • 1: OPEN
    • 2: CLOSING
    • 3: CLOSED
  • bufferedAmount: The number of bytes of data that have been queued for sending to the server but haven't yet been transmitted.

Example: Simple Chat Application

Client-side (JavaScript):

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

socket.onopen = () => {
  console.log('Connected to chat server');
};

socket.onmessage = (event) => {
  const message = event.data;
  const chatLog = document.getElementById('chat-log');
  const newMessage = document.createElement('p');
  newMessage.textContent = message;
  chatLog.appendChild(newMessage);
};

socket.onclose = (event) => {
  console.log('Disconnected from chat server');
};

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

document.getElementById('send-button').addEventListener('click', () => {
  const message = document.getElementById('message-input').value;
  socket.send(message);
  document.getElementById('message-input').value = '';
});

HTML (client-side):

<div id="chat-log"></div>
<input type="text" id="message-input">
<button id="send-button">Send</button>

Server-side (Node.js example using ws library):

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: ${message}`);
    // Broadcast the message to all connected clients
    wss.clients.forEach(client => {
      if (client !== ws && client.readyState === WebSocket.OPEN) {
        client.send(message);
      }
    });
  });

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

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

Considerations

  • Security: Use wss:// for encrypted connections, especially when transmitting sensitive data. Implement proper authentication and authorization mechanisms.
  • Error Handling: Robustly handle errors and connection closures. Implement reconnection logic if necessary.
  • Data Format: Choose a suitable data format for exchanging messages (e.g., JSON, Protocol Buffers).
  • Scalability: For high-traffic applications, consider using a WebSocket server framework that supports horizontal scaling.
  • Browser Compatibility: WebSockets are widely supported by modern browsers. However, older browsers may require polyfills.

This provides a solid foundation for understanding and using WebSockets in JavaScript. Remember to consult the official documentation for the WebSocket API and any server-side WebSocket libraries you choose to use.