En el artículo anterior hicimos un ejemplo con Server-Sent Events. Ahora vamos a ver como hacerlo utilizando WebSockets y así poder enviar y recibir mensajes desde ambos extremos, tanto desde el cliente como desde el servidor.
Estructura principal del ejemplo
Lo primero es crear la estructura general del ejercicio. En este caso vamos a hacer dos servidores: server
escuchando en el puerto 4321
y client
escuchando en el puerto 8080
. Observa que estamos creando un servidor de WebSockets con la librería ws
, pasándole el servidor server
que creamos como parámetro:
import { createServer } from "node:http";
import { readFile } from "node:fs/promises";
import { WebSocketServer } from "ws";
const SERVER_PORT = 4321;
const CLIENT_PORT = 8080;
// Server
const server = createServer();
const wss = new WebSocketServer({ server });
server.listen(SERVER_PORT, () => console.log(`Servidor en http://localhost:${SERVER_PORT}`));
// Client
const client = createServer( /* ... */ );
client.listen(CLIENT_PORT, () => console.log(`Cliente en http://localhost:${CLIENT_PORT}`));
Atento a wss
, que es la instancia del servidor de Web Socket Server con la que vamos a estar trabajando a continuación.
El servidor
Vamos ahora a implementar el servidor. Observa que todo ocurre dentro del evento connection
, que se produce cuando se realiza una conexión de un cliente con el servidor de WebSockets (lo veremos más adelante, en El cliente).
En ese fragmento de código, lo más importante es el objeto ws
que tenemos como parámetro, que es el WebSocket
en cuestión:
wss.on("connection", (ws) => {
console.log("Client connected...");
setInterval(() => {
const data = {
type: "message",
message: `Hola desde el servidor!`,
timestamp: new Date().toISOString(),
};
ws.send(JSON.stringify(data));
}, 5000);
ws.on("error", error => console.error("ERROR: ", error));
ws.on("message", data => console.log("Received: ", JSON.parse(data)));
});
Una vez el cliente se conecte, ejecutamos un setInterval()
que enviará mensajes al cliente cada 5 segundos utilizando ws.send()
. Además, estará escuchando mensajes de error y mensajes recibidos desde el cliente, para mostrarlos por consola.
El cliente
Por otro lado, recuerda que en este ejemplo estamos abriendo un cliente y un servidor. El cliente está escuchando en el puerto 8080
, por lo que si accedemos a http://localhost:8080´ nos abrirá el fichero
index.html`, similar a como hicimos en el post Devolviendo una página estática.
Dicha página index.html
tiene el siguiente contenido:
- 1️⃣ Un HTML donde tenemos un
<h1>
, un<button>
y un contenedor para los mensajes. - 2️⃣ Una etiqueta
<style>
donde tenemos los estilos CSS básicos de la página. - 3️⃣ Una etiqueta
<script>
donde tenemos la funcionalidad del cliente.
const ws = new WebSocket("ws://localhost:4321");
const container = document.querySelector(".container");
const button = document.querySelector("button");
const addMessage = (message) => {
container.innerHTML += `<div>${message}</div>`;
}
ws.addEventListener("open", () => addMessage("Conectado"));
ws.addEventListener("message", ({ data }) => addMessage(`Recibido: ${data}`));
ws.addEventListener("close", () => addMessage("Desconectado"));
ws.addEventListener("error", () => addMessage("Error"));
button.addEventListener("click", () => {
const data = {
type: "message",
message: "Hello from client!",
timestamp: new Date().toISOString(),
}
ws.send(JSON.stringify(data));
});
<h1>Start</h1>
<button>Send message</button>
<div class="container"></div>
<style>
body {
background: indigo;
color: white;
font-size: 2rem;
}
</style>
En la etiqueta <script>
lo que hacemos es conectarnos al servidor WebSocket ws://localhost:4321
que iniciamos en el apartado anterior.
Observa que escuchamos los eventos open
, message
, close
y error
. Cuando se produzca uno de ellos, añadiremos un mensaje en la página. Además, cuando hagamos click en el botón de la página, enviaremos un mensaje al servidor utilizando ws.send()
.
Como puedes ver, un ejemplo sencillo que nos permite enviar mensajes desde el servidor al cliente y desde el cliente al servidor. Ten en cuenta que incluso podrían ser aplicaciones diferentes.