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.
URL | Descripció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:
Propiedad | Descripción |
---|---|
binaryType | Define como recibir los datos: blob (binarios, por defecto) o arraybuffer . |
bufferedAmount | Cantidad de datos en cola (en bytes). |
readyState | Estado del websocket. 0 conectando, 1 conectado, 2 cerrando, 3 desconectado. |
extensions | Listado de extensiones seleccionadas por el servidor. |
protocol | El subprotocolo elegido por el servidor, si se indicó al conectar. |
url | La 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 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étodo | Descripción |
---|---|
send(data) | Envía datos al servidor a través del websocket. Puede ser |
close() | Cierra la conexión. |
close(code, reason) | Idem. Opcionalmente, puedes indicar un |
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
:
Evento | Descripción | Clase Evento |
---|---|---|
open | Se dispara cuando una conexión vía websocket se establece. | Event |
message | Se dispara cuando recibes datos del servidor vía websocket. | MessageEvent |
error | Se dispara cuando se produce una desconexión por un error. | Event |
close | Se 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.