Las conexiones TCP pueden morir en silencio. Un router se reinicia, un móvil cambia de celda, caduca una entrada de la tabla NAT… y ninguno de los dos extremos recibe un cierre limpio. El mecanismo de keep alive de MQTT existe para que tanto el cliente como el broker detecten una conexión muerta en un tiempo predecible, y para que el broker pueda publicar el mensaje Last Will del cliente cuando ocurra. Este artículo explica con exactitud cómo funciona el keep alive en MQTT 3.1.1 y 5, y cómo elegir un valor adecuado para tu red.
Qué es el intervalo de keep alive
El keep alive es un valor de 16 bits, en segundos, que el cliente envía en su paquete CONNECT. Es una promesa: «Nunca estaré en silencio más tiempo que esto». El valor máximo es 65535 segundos (algo más de 18 horas), y 0 desactiva el mecanismo por completo.
Lo fundamental es que el temporizador se refiere a cualquier paquete de control, no solo a los pings. Un cliente que publica una lectura de sensor cada 10 segundos con un keep alive de 60 segundos nunca necesita enviar un ping, porque los paquetes PUBLISH ya demuestran que está vivo. Los pings solo se envían cuando el cliente no tiene nada más que decir.
PINGREQ y PINGRESP
Cuando el periodo de keep alive está a punto de agotarse sin que el cliente haya enviado nada, envía un PINGREQ. El broker debe responder con un PINGRESP. Ambos paquetes son diminutos: solo una cabecera fija de 2 bytes, sin cabecera variable ni payload.
| Paquete | Dirección | Bytes en la red | Propósito |
|---|---|---|---|
PINGREQ | Cliente → broker | 0xC0 0x00 | Demuestra que el cliente está vivo; pregunta si el broker lo está |
PINGRESP | Broker → cliente | 0xD0 0x00 | Demuestra que el broker y el camino de vuelta al cliente funcionan |
Los dos bytes de MQTT no lo son todo: TCP/IP, los registros TLS y, en navegadores, el framing de WebSocket añaden sobrecarga a cada ping. Si estás dimensionando un plan de datos móviles, la calculadora de tamaño de paquetes MQTT te ayuda a estimar la parte MQTT de cada paquete para que sumes encima la sobrecarga de tu transporte.
La regla de 1,5×: cuándo se da una conexión por muerta
Cada extremo usa el keep alive de forma distinta:
- Lado del broker: si el broker no recibe ningún paquete de control del cliente en una vez y media el periodo de keep alive, debe cerrar la conexión de red como si la red hubiera fallado. Con un keep alive de 60 segundos, eso son 90 segundos de silencio.
- Lado del cliente: si el cliente envía un
PINGREQy no recibe unPINGRESPen un tiempo razonable, debe cerrar él mismo la conexión. Las librerías suelen tratar como fallo una respuesta de ping que no ha llegado en el siguiente tic del keep alive y empiezan a reconectar.
Como el broker trata un timeout de keep alive como una desconexión anómala, se publica el Last Will del cliente. Esto es lo que hace funcionar el clásico patrón de «dispositivo offline»: define un will retenido offline en devices/42/status y el broker anunciará el fallo por ti, como mucho 1,5 periodos de keep alive después de que el dispositivo desaparezca.
Server Keep Alive en MQTT 5
En MQTT 3.1.1 el cliente decide el keep alive y el broker tiene que aceptarlo (o rechazar la conexión). MQTT 5 añade la propiedad Server Keep Alive al CONNACK. Si el broker la incluye, el cliente debe usar el valor del broker en lugar del que pidió. Los brokers la usan para impedir que los clientes soliciten un keep alive de 0 o de muchas horas, lo que permitiría que se acumularan conexiones muertas.
MQTT 5 también hace explícito el fallo: antes de cerrar, un broker puede enviar un DISCONNECT con el reason code 0x8D (Keep Alive timeout). Puedes consultar ese y todos los demás códigos en la referencia de reason codes de MQTT. Consulta MQTT 5 vs 3.1.1 para ver el resto de diferencias del protocolo.
Los brokers también suelen imponer límites en el lado del servidor. Mosquitto, por ejemplo, tiene la opción max_keepalive: los clientes MQTT 5 reciben el valor limitado a través de Server Keep Alive, mientras que los clientes MQTT 3.1.1 que pidan más pueden ser rechazados.
Configurar el keep alive en los clientes más comunes
La mayoría de las librerías usan 60 segundos por defecto. Así se cambia:
# -k sets the keep alive in seconds
mosquitto_sub -h broker.hivemq.com -t 'testmqtt/ka-demo/#' -k 30 -vimport mqtt from 'mqtt';
const client = mqtt.connect('wss://broker.hivemq.com:8884/mqtt', {
keepalive: 30, // seconds; 0 disables
reconnectPeriod: 2000 // ms between reconnect attempts
});import paho.mqtt.client as mqtt
client = mqtt.Client(mqtt.CallbackAPIVersion.VERSION2)
client.connect("broker.hivemq.com", 1883, keepalive=30)
client.loop_forever()¿Quieres el snippet completo para tu lenguaje? El generador de código MQTT produce código de conexión con el keep alive ya configurado. Para ver el comportamiento en directo, conecta el cliente MQTT online con un keep alive corto y déjalo inactivo.
Pruébalo en el cliente MQTT online
NAT, firewalls y redes móviles
El keep alive tiene una segunda función que la especificación no menciona: mantener contentos a los equipos intermedios. Las pasarelas NAT, el NAT de operador (CGNAT) de las redes móviles, los balanceadores de carga y los firewalls con estado descartan los flujos TCP inactivos tras un timeout. Una vez desaparecida la asociación, el cliente sigue creyendo que está conectado, pero nada de lo que envía llega al broker y nada de lo que envía el broker llega al cliente.
- Mantente por debajo del timeout de inactividad más corto del camino. Los balanceadores cloud y los operadores móviles suelen usar timeouts de inactividad de pocos minutos, a veces menos. Un keep alive holgadamente por debajo mantiene viva la asociación.
- No te fíes del TCP keepalive. Los valores por defecto del TCP keepalive del sistema operativo suelen medirse en horas, demasiado lentos para proteger una sesión MQTT.
- Busca el equilibrio con la batería. Cada ping despierta la radio. En dispositivos celulares alimentados por batería, intervalos más largos combinados con una sesión persistente (para que los mensajes perdidos queden en cola) suelen ser la mejor opción.
- Los WebSockets no son una excepción. Los clientes de navegador detrás de proxies sufren los mismos timeouts de inactividad; el keep alive de MQTT funciona igual con MQTT sobre WebSockets.
Cómo elegir un valor de keep alive
| Escenario | Keep alive recomendado | Por qué |
|---|---|---|
| Dashboards y servicios de backend en redes estables | 30–60 s | Valor por defecto de las librerías, poca sobrecarga, detección en aproximadamente un minuto |
| Dispositivos cuyo estado «offline» debe verse rápido | 10–20 s | El will se dispara en unos 15–30 s |
| Redes celulares o con mucho NAT | Por debajo del timeout de inactividad más corto del camino | Evita conexiones medio abiertas silenciosas |
| Dispositivos a batería que reportan con poca frecuencia | Varios minutos, o conectar–publicar–desconectar | Menos activaciones de la radio; apóyate en sesiones persistentes |
Solucionar desconexiones por keep alive
Si los clientes se desconectan cada 1,5× el keep alive, la causa habitual es un bucle del cliente que no se está ejecutando: por ejemplo, el network loop de Paho bloqueado por un trabajo largo dentro de un callback, de modo que no se envía ningún PINGREQ. En cambio, las reconexiones frecuentes a un intervalo fijo de unos pocos minutos apuntan a un timeout de inactividad en un proxy o NAT. Para otras causas, consulta la guía de errores de conexión MQTT.
Preguntas frecuentes
¿Cuál es un buen intervalo de keep alive en MQTT?
Para la mayoría de los dispositivos en redes estables, entre 30 y 60 segundos es un valor por defecto sensato. Usa valores más cortos (de 15 a 30 segundos) cuando necesites detectar rápido clientes muertos o debas quedarte por debajo de un timeout de NAT agresivo, y valores más largos en dispositivos a batería, donde cada activación de la radio cuesta energía.
¿Qué pasa cuando vence el keep alive de MQTT?
Si el broker no recibe ningún paquete de control del cliente en una vez y media el intervalo de keep alive, cierra la conexión de red y la trata como una desconexión anómala. Eso significa que se publica el mensaje Last Will, si lo hay. En MQTT 5 el broker puede enviar antes un DISCONNECT con el reason code 0x8D (Keep Alive timeout).
¿Poner el keep alive a 0 lo desactiva?
Sí. Un keep alive de 0 desactiva el mecanismo, así que el broker nunca desconecta al cliente por inactividad. Rara vez es buena idea, porque las conexiones TCP medio abiertas pueden permanecer mucho tiempo sin que nadie lo note.