Cuando cliente y servidor intercambian datos por WebSockets, no lo hacen como simples textos crudos, sino mediante una estructura definida en frames (tramas) según la especificación del protocolo (RFC 6455).
Un mensaje WebSocket puede estar compuesto de uno o varios frames, que son los bloques de datos que viajan por la conexión.
Conceptos clave
Frame (trama)
- Unidad mínima de transmisión en WebSockets.
- Cada mensaje se encapsula en uno o más frames.
Mensaje
- Puede ser de texto, binario o de control.
- Un mensaje puede estar formado por varios frames consecutivos.
Bidireccionalidad
- Tanto cliente como servidor pueden enviar frames en cualquier momento.
Tipos de mensajes
Texto
- Contenido en formato UTF‑8.
- Es el más usado para enviar JSON u otros datos legibles.
Binarios
- Secuencias de bytes.
- Útiles para imágenes, audio, video o datos en bruto.
Control
- Mensajes internos del protocolo.
- Ejemplos: Close (cerrar conexión), Ping (comprobar vida), Pong (respuesta a ping).
Estructura de un frame WebSocket
Cada frame está compuesto por varios campos:
FIN RSV1 RSV2 RSV3 OPCODE MASK Payload length [Extended length] [Masking key] Payload data
Desglose
- FIN (1 bit): indica si este frame es el último de un mensaje (1) o si vienen más (0).
- RSV1‑3 (3 bits): reservados para extensiones (p.ej., compresión). Normalmente en 0.
- Opcode (4 bits): tipo de frame:
- 0x1: Texto
- 0x2: Binario
- 0x8: Cierre de conexión
- 0x9: Ping
- 0xA: Pong
- Máscara (MASK) (1 bit): indica si los datos están enmascarados.
- Obligatorio en frames enviados por el cliente (seguridad básica ante inyección).
- Opcional en frames del servidor.
- Payload length (7 bits o más): tamaño del contenido.
- Si ≤ 125: el tamaño va en estos 7 bits.
- Si = 126: se usan 2 bytes adicionales (16 bits).
- Si = 127: se usan 8 bytes adicionales (64 bits).
- Masking key (32 bits, opcional): solo presente si la máscara está activa; se usa para desofuscar el payload aplicando XOR.
- Payload data: el contenido real del mensaje (texto, binario, etc.).
Ejemplo de flujo de mensajes
Cliente envía texto
const ws = new WebSocket("ws://localhost:8080");
ws.onopen = () => {
ws.send("Hola servidor!"); // Se envía como frame de tipo texto (opcode 0x1)
};
El navegador empaqueta "Hola servidor!"
en un frame con:
- FIN = 1 (mensaje completo en un solo frame)
- OPCODE = 0x1 (texto)
- Payload length = número de bytes
- Payload data = bytes en UTF‑8
Servidor responde con JSON
ws.send(JSON.stringify({ tipo: "saludo", mensaje: "Bienvenido!" }));
El JSON viaja como frame de texto. El cliente lo recibe en onmessage
y lo interpreta como string
para luego JSON.parse
si es necesario.
Ping/Pong
Los ping/pong se usan para comprobar si la conexión sigue activa:
- Servidor envía Ping.
- Cliente responde automáticamente con Pong.
Esto ocurre por debajo sin que el programador tenga que intervenir, salvo en librerías de bajo nivel.
Resumen visual
[Frame]
Cabecera: FIN + OPCODE + Máscara + Longitud
(Opcional) Clave de máscara
Datos (Payload)
Ejemplo de intercambio (consola navegador):
Cliente → "Hola servidor!"
Servidor → {"tipo":"saludo","mensaje":"Bienvenido!"}
Servidor → Ping
Cliente → Pong
En resumen
- Todo en WebSockets viaja en frames.
- Los mensajes pueden ser de texto, binarios o de control.
- El cliente siempre enmascara sus mensajes; el servidor no necesariamente.
- La estructura compacta permite comunicaciones eficientes en tiempo real.