Comunicación en Tiempo Real

WebSockets

WebSockets mantienen una conexión abierta en ambas direcciones, permitiendo que el servidor envíe datos en el momento en que cambian en lugar de esperar a que se le soliciten.

intermediate14 min readUpdated 15 sept 2026
server.js
js
// server.js
import { WebSocketServer } from "ws";

const wss = new WebSocketServer({ port: 8080 });

wss.on("connection", (socket) => {
  socket.on("message", (data) => {
    for (const client of wss.clients) {
      if (client.readyState === client.OPEN) {
        client.send(data.toString());
      }
    }
  });
});
Iniciado por
Handshake de HTTP Upgrade
Transporte
Una conexión TCP
Dirección
Full duplex
Overhead
Muy bajo por mensaje
Seguro
wss:// sobre TLS
Alternativas
SSE, long polling

Por que importa

Por qué son importantes los WebSockets

El servidor puede hacer push

El servidor envía datos en el instante en que ocurren, sin necesidad de una solicitud del cliente, lo cual es esencial para chats, presencia y datos en vivo.

Baja latencia y overhead

Después del handshake, los mensajes tienen un overhead de framing mínimo y no repiten cabeceras, a diferencia del polling.

Compatible con la plataforma web

La API del navegador es sencilla, funciona sobre los mismos puertos que HTTP y se actualiza a TLS mediante wss.

La imagen completa

Las tres partes de un WebSocket

Un upgrade de HTTP, una conexión full-duplex basada en frames y la lógica de aplicación para salas, presencia y reconexión.

El handshake

Upgrade

Una solicitud HTTP con una cabecera Upgrade se convierte en una conexión WebSocket.

Frames

Intercambio

Mensajes pequeños estructurados en frames viajan en ambas direcciones sobre una sola conexión.

El servidor

Gestión

Rastrea conexiones, salas y presencia, y escala mediante pub/sub.

WebSockets de un vistazo

El núcleo de WebSockets

Handshake de Upgrade

El cliente solicita actualizar una solicitud HTTP; el servidor responde con un código 101.

Frames

Mensajes de texto y binarios con una cabecera pequeña, además de frames de control.

Full duplex

Ambos extremos pueden enviar datos en cualquier momento sobre la misma conexión.

Salas (Rooms)

Agrupa conexiones para que los mensajes lleguen al subconjunto correcto de usuarios.

Heartbeats

El ping y el pong detectan conexiones muertas y evitan que los proxies las cierren.

Pub/sub

Difunde mensajes entre instancias del servidor a través de Redis u otro broker.

Una breve historia

Del polling a las conexiones persistentes

  1. 2008

    Primeros hacks

    Los desarrolladores simulan el tiempo real con long polling y técnicas de comet.

    08
  2. 2011

    Estandarización de WebSocket

    El RFC 6455 define el protocolo y los navegadores añaden soporte.

    11
  3. 2012

    Socket.IO lo populariza

    Una librería con fallbacks y salas lleva el tiempo real a las aplicaciones mainstream.

    12
  4. 2015

    Patrones de escalabilidad

    Redis pub/sub y las sticky sessions se convierten en el estándar para despliegues multi-instancia.

    15
  5. Hoy

    Una herramienta entre varias

    WebSockets coexisten con Server-Sent Events, WebTransport y WebRTC.

    Hoy

La guia completa

WebSockets: Todo lo que necesitas saber

¿Qué son los WebSockets?

Un WebSocket es una conexión persistente y full-duplex entre un cliente y un servidor. Tras un handshake inicial de HTTP, ambas partes pueden enviar mensajes en cualquier momento a través de la misma conexión TCP, con una sobrecarga muy baja por cada mensaje. El servidor puede enviar datos en el instante en que algo cambia, en lugar de esperar a que el cliente los solicite.

Esto convierte a los WebSockets en la herramienta ideal para chats, indicadores de presencia, dashboards en tiempo real, edición colaborativa, juegos multijugador y cualquier otra funcionalidad donde las actualizaciones deban llegar de inmediato. Para actualizaciones sencillas en una sola dirección, los Server-Sent Events suelen ser una opción más simple.

El handshake de actualización

Una conexión WebSocket comienza su vida como una solicitud HTTP.

GET /chat HTTP/1.1
Host: example.com
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==
Sec-WebSocket-Version: 13

Si el servidor la acepta, responde:

HTTP/1.1 101 Switching Protocols
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=

Después de 101 Switching Protocols, la conexión deja de ser HTTP. Se convierte en un WebSocket y los datos fluyen en frames. Debido a que comienza como HTTP, funciona a través de los mismos puertos (80 y 443), pasa por la mayoría de los proxies y firewalls, y se actualiza a TLS mediante wss://.

Frames

Los mensajes de WebSocket se dividen en frames con un encabezado pequeño. Existen frames de texto, frames binarios y frames de control para ping, pong y close. El framing es mínimo, razón por la cual los mensajes de WebSocket son mucho más eficientes que las solicitudes HTTP repetidas.

El protocolo también define códigos de cierre, como 1000 para un cierre normal y 1001 para “going away”, los cuales ayudan a ambas partes a entender por qué terminó una conexión.

La API del navegador

La API del cliente es pequeña y está basada en eventos.

// client.js
const socket = new WebSocket("wss://api.example.com/chat");

socket.addEventListener("open", () => {
  socket.send(JSON.stringify({ type: "join", room: "general" }));
});

socket.addEventListener("message", (event) => {
  const message = JSON.parse(event.data);
  render(message);
});

socket.addEventListener("close", (event) => {
  console.log("closed", event.code, event.reason);
});

socket.addEventListener("error", () => {
  console.error("connection error");
});

Envías datos con socket.send() y recibes eventos message. La propiedad readyState te indica si el socket se está conectando, está abierto, cerrándose o cerrado.

Un servidor Node.js

La librería ws es la forma estándar de ejecutar un servidor WebSocket en Node.

// server.js
import { WebSocketServer } from "ws";

const wss = new WebSocketServer({ port: 8080 });

wss.on("connection", (socket, request) => {
  socket.on("message", (data, isBinary) => {
    for (const client of wss.clients) {
      if (client !== socket && client.readyState === client.OPEN) {
        client.send(data, { binary: isBinary });
      }
    }
  });

  socket.on("close", () => console.log("client left"));
});

El servidor rastrea cada conexión y puede enviar mensajes a un cliente, a un subconjunto (una sala) o a todos ellos. Socket.IO es una alternativa de más alto nivel que añade salas, confirmaciones (acknowledgements), reconexión automática y fallbacks, a cambio de utilizar un protocolo y un cliente personalizados.

Salas y presencia

Las aplicaciones reales rara vez emiten mensajes a todo el mundo. En su lugar, rastreas a qué usuario o sala pertenece cada conexión.

// rooms.js
const rooms = new Map();

function join(socket, room) {
  if (!rooms.has(room)) rooms.set(room, new Set());
  rooms.get(room).add(socket);
}

function broadcast(room, payload) {
  for (const socket of rooms.get(room) ?? []) {
    if (socket.readyState === socket.OPEN) socket.send(payload);
  }
}

Las salas, la presencia (quién está conectado) y los indicadores de escritura se construyen basándose en este patrón, sumando heartbeats para detectar las desconexiones.

Heartbeats y reconexión

Una conexión puede morir sin que ninguna de las partes se dé cuenta, especialmente en redes móviles o detrás de proxies. Hay dos cosas que ayudan a mantenerla saludable:

  • Heartbeats. Envía un ping cada 30 segundos y termina la conexión si no llega un pong. Muchos proxies también cierran las conexiones inactivas, por lo que el tráfico constante las mantiene abiertas.
  • Reconexión. El cliente debe reconectarse utilizando un exponential backoff y restablecer sus suscripciones. Aquí es donde una librería como Socket.IO ahorra mucho esfuerzo.
// heartbeat.js
const interval = setInterval(() => {
  for (const socket of wss.clients) {
    if (socket.isAlive === false) return socket.terminate();
    socket.isAlive = false;
    socket.ping();
  }
}, 30_000);

wss.on("close", () => clearInterval(interval));

Escalabilidad

Una conexión WebSocket reside en una única instancia de servidor, por lo que para realizar un broadcast entre instancias es necesario un canal compartido.

  • Pub/sub: publica los mensajes en Redis, NATS u otro broker, y haz que cada instancia los entregue a sus clientes locales.
  • Sticky sessions: si tu infraestructura lo requiere, mantén al cliente en la misma instancia durante toda la vida de la conexión.
  • Estado en un almacenamiento compartido: mantén la presencia y la membresía de las salas en Redis para que cualquier instancia pueda responder.
  • Backpressure y límites: limita las conexiones por instancia y monitoriza la memoria, ya que cada conexión mantiene un socket y cierto estado.

Seguridad

  • Utiliza siempre wss:// en producción para que el tráfico esté cifrado.
  • Autentica durante el handshake y rechaza las conexiones no autorizadas.
  • Valida el header Origin para prevenir el cross-site WebSocket hijacking.
  • Autoriza cada mensaje; nunca asumas que un cliente conectado tiene permiso para realizar una acción.
  • Limita el tamaño y la frecuencia de los mensajes para evitar abusos.
  • Configura timeouts para evitar que las conexiones abandonadas provoquen fugas de memoria.

Mejores prácticas

  • Usa WebSockets para actualizaciones bidireccionales de baja latencia y SSE para el envío de datos unidireccional.
  • Implementa heartbeat y reconexión; nunca asumas que una conexión sigue activa.
  • Autentica durante el handshake y autoriza cada mensaje.
  • Mantén el estado por conexión reducido y gestiona las salas en un store compartido.
  • Usa pub/sub para realizar broadcasts entre instancias.
  • Establece límites de tamaño de mensaje y de tasa de envío (rate limits).
  • Finaliza las conexiones inactivas y monitorea el número de conexiones.

Errores comunes

  • Usar WebSockets para peticiones y respuestas simples donde HTTP sería suficiente.
  • Olvidar los heartbeats y provocar fugas de conexiones inactivas.
  • Hacer broadcast a todos los clientes cuando solo una sala debería recibir el mensaje.
  • Almacenar el estado autoritativo únicamente en memoria y perderlo al reiniciar.
  • Omitir las comprobaciones de origen y la autorización de los mensajes.
  • Asumir que el load balancer mantendrá la conexión sin la configuración adecuada.

Próximos pasos

Los WebSockets son la capa de conexión persistente sobre TCP. Refuerza estos conceptos con la guía de HTTP y la de TCP/IP & Sockets, ejecútalos en Node.js y compara el modelo push con las GraphQL subscriptions. Después, construye una pequeña sala de chat con canales y heartbeats para ver estos patrones en la práctica.

Recibir actualizaciones en vivo

Un WebSocket envía los cambios mientras ocurren. El polling repite solicitudes y desperdicia ancho de banda, especialmente cuando nada cambia.

Preferir
const socket = new WebSocket("wss://api.example.com/feed");

socket.addEventListener("message", (event) => {
  render(JSON.parse(event.data));
});
Evitar
setInterval(async () => {
  const res = await fetch("/api/feed");
  render(await res.json());
}, 1000);

Detectar conexiones muertas

Una conexión caída suele ser invisible para ambos extremos. Los heartbeats la detectan y activan la reconexión.

Preferir
const alive = setInterval(() => {
  if (socket.readyState !== socket.OPEN) return;
  socket.ping();
}, 30_000);
Evitar
// assume the connection is
// alive forever; dead peers
// leak resources silently

Preguntas frecuentes

Preguntas frecuentes

Keep learning

Related topics from the roadmap.

$ comienza a aprender

Listo para aprender WebSockets?

Nuestro tutorial interactivo te guia a traves de WebSockets paso a paso — con quizzes y codigo real que puedes ejecutar en el navegador.