Cómo funciona
El canal bidireccional entre el servidor Socket.IO (Node.js) y el cliente Socket.IO (navegador, Node.js, u otro lenguaje de programación) se establece con una conexión WebSocket siempre que sea posible, y usará HTTP long-polling como respaldo.
El código base de Socket.IO está dividido en dos capas distintas:
- la plomería de bajo nivel: lo que llamamos Engine.IO, el motor dentro de Socket.IO
- la API de alto nivel: Socket.IO en sí
Engine.IO
Engine.IO es responsable de establecer la conexión de bajo nivel entre el servidor y el cliente. Se encarga de:
- los diversos transportes y el mecanismo de actualización
- la detección de desconexión
Una versión detallada del protocolo Engine.IO se puede encontrar aquí.
El código fuente de la implementación de referencia (escrita en TypeScript) se puede encontrar aquí:
- servidor: https://github.com/socketio/engine.io
- cliente: https://github.com/socketio/engine.io-client
- parser: https://github.com/socketio/engine.io-parser
Transportes
Actualmente hay dos transportes implementados:
HTTP long-polling
El transporte HTTP long-polling (también llamado simplemente "polling") consiste en solicitudes HTTP sucesivas:
- solicitudes
GETde larga duración, para recibir datos del servidor - solicitudes
POSTde corta duración, para enviar datos al servidor
Debido a la naturaleza del transporte, emisiones sucesivas pueden concatenarse y enviarse dentro de la misma solicitud HTTP.
WebSocket
El transporte WebSocket consiste, bueno, en una conexión WebSocket, que proporciona un canal de comunicación bidireccional y de baja latencia entre el servidor y el cliente.
Debido a la naturaleza del transporte, cada emisión se envía en su propio frame WebSocket (algunas emisiones pueden incluso resultar en dos frames WebSocket distintos, más información aquí).
Handshake
Al comienzo de la conexión Engine.IO, el servidor envía alguna información:
{
"sid": "FSDjX-WRwSA4zTZMALqx",
"upgrades": ["websocket"],
"pingInterval": 25000,
"pingTimeout": 20000,
"maxPayload": 1000000
}
- el
sides el ID de la sesión, debe incluirse en el parámetro de consultasiden todas las solicitudes HTTP posteriores - el array
upgradescontiene la lista de todos los transportes "mejores" que son soportados por el servidor - los valores
pingIntervalypingTimeoutse usan en el mecanismo de heartbeat - el valor
maxPayloadindica el número máximo de bytes por paquete aceptado por el servidor
Mecanismo de actualización
Por defecto, el cliente establece la conexión con el transporte HTTP long-polling.
Pero, ¿por qué?
Aunque WebSocket es claramente la mejor manera de establecer una comunicación bidireccional, la experiencia ha demostrado que no siempre es posible establecer una conexión WebSocket, debido a proxies corporativos, firewalls personales, software antivirus...
Desde la perspectiva del usuario, una conexión WebSocket fallida puede traducirse en hasta 10 segundos de espera para que la aplicación en tiempo real comience a intercambiar datos. Esto perceptiblemente afecta la experiencia del usuario.
En resumen, Engine.IO se enfoca primero en la fiabilidad y la experiencia del usuario, y segundo en mejoras marginales potenciales de UX y mayor rendimiento del servidor.
Para actualizar, el cliente:
- se asegura de que su búfer de salida esté vacío
- pone el transporte actual en modo de solo lectura
- intenta establecer una conexión con el otro transporte
- si tiene éxito, cierra el primer transporte
Puedes verificar en el Monitor de Red de tu navegador:

- handshake (contiene el ID de sesión — aquí,
zBjrh...AAAK— que se usa en las solicitudes posteriores) - enviar datos (HTTP long-polling)
- recibir datos (HTTP long-polling)
- actualización (WebSocket)
- recibir datos (HTTP long-polling, cerrado una vez que la conexión WebSocket en 4. se establece exitosamente)
Detección de desconexión
La conexión Engine.IO se considera cerrada cuando:
- una solicitud HTTP (ya sea GET o POST) falla (por ejemplo, cuando el servidor se apaga)
- la conexión WebSocket se cierra (por ejemplo, cuando el usuario cierra la pestaña en su navegador)
socket.disconnect()se llama en el lado del servidor o del cliente
También hay un mecanismo de heartbeat que verifica que la conexión entre el servidor y el cliente sigue activa y funcionando:
En un intervalo dado (el valor pingInterval enviado en el handshake) el servidor envía un paquete PING y el cliente tiene unos segundos (el valor pingTimeout) para enviar un paquete PONG de vuelta. Si el servidor no recibe un paquete PONG de vuelta, considerará que la conexión está cerrada. De manera inversa, si el cliente no recibe un paquete PING dentro de pingInterval + pingTimeout, considerará que la conexión está cerrada.
Las razones de desconexión se enumeran aquí (lado del servidor) y aquí (lado del cliente).
Socket.IO
Socket.IO proporciona algunas características adicionales sobre la conexión Engine.IO:
- reconexión automática
- almacenamiento en búfer de paquetes
- confirmaciones
- broadcasting a todos los clientes o a un subconjunto de clientes (lo que llamamos "Room")
- multiplexación (lo que llamamos "Namespace")
Una versión detallada del protocolo Socket.IO se puede encontrar aquí.
El código fuente de la implementación de referencia (escrita en TypeScript) se puede encontrar aquí: