13. WebSockets

Unlike Fetch data or AJAX, the WebSocket API is an advanced technology that lets us do bidirectional communication between a client and a server. The server can send messages to the client and the client to the server through the same channel.

AJAX vs WebSockets

This technology is essential for tasks like:

  • Real time applications, such as a chat or a social network.
  • Sending messages to a specific client, such as notifications.
  • Sending mass messages, such as a multiplayer video game.
  • Collaboration applications, such as Google Drive or Notion.

Server

To illustrate WebSocket's features without relying on complex or paid services, we'll create a small server. This lesson doesn't aim to teach backend development with JavaScript or dive deep into Node, but rather to offer a basic tool for learning WebSockets.

We'll create a server that sends back any information sent to it to all connected clients (Broadcast).

Broadcast

First, let's make sure we have Node.js (the binary for running JavaScript) and npm (the JavaScript package manager) installed on our machine.

node --version
npm --version

In both cases it will show the installed version. Otherwise it will show an error that we'll need to fix before continuing.

Now we'll install the WebSockets libraries.

We'll place ourselves in a new folder, so as not to mix it with other work, and run.

npm install ws

We create a file called index.js, in the folder we're in, and paste the following code.

// Imports
const WebSocket = require("ws");
const http = require("http");

// We create an instance of the HTTP (Web) server
const server = http.createServer();
// We create and start a WebSocket server from the HTTP server
const wss = new WebSocket.Server({ server });

// We listen for connection events
wss.on("connection", function connection(ws) {
    // We listen for incoming messages
    ws.on("message", function incoming(data) {
        // We iterate over all connected clients
        wss.clients.forEach(function each(client) {
            if (client.readyState === WebSocket.OPEN) {
                // We send the received information
                client.send(data.toString());
            }
        });
    });
});

// We start the HTTP server
server.listen(8080);
console.log("Server running. Use ws://localhost:8080 to connect.")

We save.

Now we'll start the server.

node index.js

Done! To connect with the client we'll use ws://localhost:8080. We'll leave it running and move on to creating a client.

Client

Connecting

Connecting WebSocket

Natively we have an API for handling these interactions. We'll need to use the WebSocket object and store it in a variable to use its events or features.

const myWebSocket = new WebSocket(url [, protocols]);

It can be used by including the protocol along with the full path.

const myWebSocket = new WebSocket("ws://myserver.com");

Or by separating the protocols to use as a second argument.

const myWebSocket = new WebSocket("myserver.com", ["ws", "wss"]);

Just like there's a difference between http and https, the latter being secure, we'll also find this in the WebSocket protocol.

  • ws: WebSockets.
  • wss: Secure WebSockets.
const myWebSocket = new WebSocket("ws://myserver.com");

Being more secure.

const myWebSocket = new WebSocket("wss://myserver.com");

We also have an event that fires when connecting.

const myWebSocket = new WebSocket("wss://myserver.com");

function open () {
    // Connection opens
    console.log("WebSocket open.");
}

myWebSocket.addEventListener("open", open);

Messages

Sending a message with WebSocket

Listening

If we want to collect messages sent by the server, or by other clients, we need to watch for the message event.

const myWebSocket = new WebSocket("wss://myserver.com");

function message (event) {
    // A message is received
    console.log("WebSocket has received a message");
    // Show message in HTML
    myResponses.innerHTML = myResponses.innerHTML.concat(event.data, "<br>");
}

myWebSocket.addEventListener("message", message);

Sending

To send messages to the server we have the send() function.

const myWebSocket = new WebSocket("wss://myserver.com");

myWebSocket.send("My message");

Error handling

Things won't always go as expected, there can be connection drops due to network outages or server issues.

Error with WebSocket

For that, we'll watch the error event.

const myWebSocket = new WebSocket("wss://myserver.com");

function error (event) {
    // An error has occurred
    console.error("WebSocket observed an error: ", event);
}

myWebSocket.addEventListener("error", error);

Disconnecting

For the case where the client disconnects or the connection breaks, possibly due to a major failure, we'll catch the close event.

Disconnecting WebSocket

const myWebSocket = new WebSocket("wss://myserver.com");

function close () {
    // Connection closes
    console.log("WebSocket closed.");
}

myWebSocket.addEventListener("close", close);

Example

With everything we've learned, we'll connect to the server to send or receive messages.

Create a file called client-ws.html with the following content.

<!doctype html>
<html lang="es">
  <head>
    <meta charset="UTF-8"/>
    <title>WebSocket client example</title>
  </head>
  <body>

    <!-- New message -->
    <input type="text" id="nuevo-mensaje">

    <!-- Received messages -->
    <div id="respuestas"></div>

    <script>
      // Variables
      const miWebSocket = new WebSocket("ws://localhost:8080");
      const miNuevoMensaje = document.querySelector("#nuevo-mensaje");
      const misRespuestas = document.querySelector("#respuestas");

      // Functions
      function open () {
          // Connection opens
          console.log("WebSocket open.");
      }

      async function message (evento) {
          // A message is received
          console.log("WebSocket has received a message");
          // Show message in HTML
          const mensajeRecibido = await evento.data.text(); // Fix for Node since it returns a Blob. With just "evento.data" it should be enough
          misRespuestas.innerHTML = misRespuestas.innerHTML.concat(mensajeRecibido, "<br>");
      }

      function error (evento) {
          // An error has occurred
          console.error("WebSocket observed an error: ", evento);
      }

      function close () {
          // Connection closes
          console.log("WebSocket closed.");
      }

      function enviarNuevoMensaje (evento) {
          // Enter key event
          if(evento.code === "Enter") {
              // Send message via WebSockets
              miWebSocket.send(miNuevoMensaje.value);
              // Clear input text
              miNuevoMensaje.value = "";
          }
      }

      // WebSocket events
      miWebSocket.addEventListener("open", open);
      miWebSocket.addEventListener("message", message);
      miWebSocket.addEventListener("error", error);
      miWebSocket.addEventListener("close", close);

      // Event to send a new message
      miNuevoMensaje.addEventListener("keypress", enviarNuevoMensaje);

    </script>
  </body>
</html>

Open the file in several windows at the same time to see how it works.

Activity 1

Create a text input to send notifications to everyone who's connected. Use the example server from the lesson.

Exercise 13-1

Nightmare level 👹

Add an alert sound when you receive a new message.

Activity 2

Build a simple, lightweight chat. Use the example server from the lesson.

Exercise 13-2

Nightmare level 👹

Unfortunately, messages are lost when we refresh the page. Fix the problem by storing each message in Localstorage and retrieving them all when you enter.

Activity 3

Program a tic-tac-toe game to play with a friend remotely. Use the example server from the lesson.

This work is under a Attribution-NonCommercial-NoDerivatives 4.0 International license.

Desafíos de programación atemporales y multiparadigmáticos

Desafíos de programación atemporales y multiparadigmáticos

Te encuentras ante un librillo de actividades, divididas en 2 niveles de dificultad. Te enfrentarás a los casos más comunes que te puedes encontrar en pruebas técnicas o aprender conceptos elementales de programación.

Buy the book

Will you buy me a coffee?

This is how I keep writing without ads or paywalls.

Comments

There are no comments yet.