Crear un cliente de WebSockets

Comunicarse con WebSockets en tiempo real


Los WebSockets son un protocolo de comunicación que permite una conexión bidireccional en tiempo real entre un cliente (navegador) y un servidor. A diferencia de HTTP, en donde se utiliza un modelo de petición-respuesta, los WebSockets permiten conectarse y enviar y recibir datos de forma continua sin necesidad de abrir nuevas conexiones, lo que lo hace ideal para comunicaciones rápidas, en tiempo real.

Conexión vía WebSocket

Para conectar, necesitaremos la dirección de un servidor de WebSockets. Vamos a imaginar que tenemos un servidor de chat en chat.manz.dev, así que creamos una instancia de WebSocket:

const client = new WebSocket("wss://chat.manz.dev:443");

Observa que la URL de conexión utiliza el protocolo wss:// y el puerto :443. El puerto es opcional, no hace falta indicarlo, ya que se asume al indicar el protocolo.

URLDescripción
ws://Conexión websocket insegura. Si se usa http:// se transforma a ws://.
wss://Conexión websocket segura. Si se usa https:// se transforma a wss://.

Opcionalmente, se puede añadir un segundo parámetro para indicar un subprotocolo o un de subprotocolos.

Propiedades del WebSocket

Una vez establecida la conexión, podemos acceder a ciertas propiedades de nuestro cliente de WebSocket, que tenemos en la constante client. Las propiedades disponibles son las siguientes:

PropiedadDescripción
binaryTypeDefine como recibir los datos: blob (binarios, por defecto) o arraybuffer.
bufferedAmountCantidad de datos en cola (en bytes).
readyStateEstado del websocket. 0 conectando, 1 conectado, 2 cerrando, 3 desconectado.
extensionsListado de extensiones seleccionadas por el servidor.
protocolEl subprotocolo elegido por el servidor, si se indicó al conectar.
urlLa URL de la conexión, indicada al conectar.

Las más relevantes serían .binaryType, mediante la cuál puedes establecer la forma de obtener información vía websockets. En un blob la información es directa (la forma que se suele utilizar, y está activada por defecto), mientras que con arraybuffer la información se obtiene en un array de bytes.

Por otro lado, readyState nos devuelve un que representa el estado de la conexión del websocket, y bufferedAmount nos devuelve la cantidad de datos en cola.

Métodos de acción

Pasemos a detalles más interesantes. Mediante los métodos del WebSocket podemos enviar datos al servidor o cerrar la conexión:

MétodoDescripción
send(data)Envía datos al servidor a través del websocket. Puede ser , o .
close()Cierra la conexión.
close(code, reason)Idem. Opcionalmente, puedes indicar un código de error y una razón.

Por ejemplo, podríamos añadir dos botones a la página, mediante los cuales se envíe información o se cierre la conexión:

const client = new WebSocket("wss://chat.manz.dev:443");
const sendButton = document.querySelector("button.send");
const closeButton = document.querySelector("button.close");

sendButton.addEventListener("click", () => {
  const data = {
    type: "message",
    message: "Hello from client!",
    timestamp: new Date().toISOString(),
  }
  const textData = JSON.stringify(data);
  client.send(textData);
});

closeButton.addEventListener("click", () => {
  client.close(1000, "Bye! Good dreams!");
});

Observa que estamos trabajando con como datos. Aunque anteriormente se dijo que los formatos posibles son o , ten en cuenta que realmente si los datos que se envían no son binarios, se recibirá un . En caso contrario, se recibirá un .

Escuchar eventos

La parte fundamental de los WebSockets se basa en escuchar eventos. Hay cuatro tipo de eventos que puedes escuchar desde un cliente de WebSockets que has instanciado: open, message, error y close:

EventoDescripciónClase Evento
openSe dispara cuando una conexión vía websocket se establece.Event
messageSe dispara cuando recibes datos del servidor vía websocket.MessageEvent
errorSe dispara cuando se produce una desconexión por un error.Event
closeSe dispara cuando se cierra la conexión websocket.CloseEvent

Veamos un ejemplo, donde escucharemos estos eventos mediante un addEventListener(). Ten en cuenta que aunque también se pueden escuchar haciendo uso de las propiedades del BOM, en frontend es recomendable utilizar los addEventListener().

Observa el siguiente ejemplo donde escuchamos los eventos open (establecer conexión), error (se corta la conexión debido a un error) o close (se cierra la conexión):

const URL = "wss://chat.manz.dev";
const client = new WebSocket(URL);
client.addEventListener("open", () => console.log("Se ha establecido la conexión."));
client.addEventListener("error", () => console.log("Se ha cortado la conexión."));
client.addEventListener("close", ({ code, reason }) => {
  console.log(`Se ha cerrado la conexión (${code}): ${reason ?? "-"}`);
});

En el último, desestructuramos code y reason para obtener información de la desconexión. Nos devolverá un código de error, y opcionalmente, una razón asociada.

Pero la parte fundamental de los eventos es el evento message, que nos llegará cuando recibamos datos desde el servidor de WebSockets. Observa el siguiente ejemplo:

const URL = "wss://chat.manz.dev";
const client = new WebSocket(URL);
client.addEventListener("message", ({ data }) => {
  console.log(`Mensaje recibido: ${data}`);
});

En este caso, hemos omitido el resto de eventos para simplificar. En los eventos message, accedemos a la propiedad data que desestructuramos. Recibiremos la información enviada por el servidor de WebSockets en el formato que haya decidido enviar, que recuerda que puede ser:

  • : Generalmente, un objeto que hemos previamente convertido en JSON con stringify()
  • : En el caso que se envíen datos binarios desde el servidor.
  • : En el caso de que se envíen array de bytes desde el servidor.

Ten en cuenta que en este artículo sólo se ha abordado la creación de clientes de WebSocket para utilizarlos en el navegador. Es necesario disponer de un servidor de WebSockets para poder realizar una comunicación. Echa un vistazo al post Usando un servidor de WebSockets en NodeJS.

¿Quién soy yo?

Soy Manz, vivo en Tenerife (España) y soy streamer partner en Twitch y profesor. Me apasiona el universo de la programación web, el diseño y desarrollo web y la tecnología en general. Aunque soy full-stack, mi pasión es el front-end, la terminal y crear cosas divertidas y locas.

Puedes encontrar más sobre mi en Manz.dev