Si tu cliente MQTT se desconecta constantemente, la causa es casi siempre una de estas cinco: dos clientes que comparten un client ID, un timeout de keep alive, la red que corta en silencio las conexiones inactivas, un paquete que el broker no acepta o un límite del plan o de ritmo. Esta guía explica cómo reconocer cada una a partir de los síntomas y los registros, y recorre un flujo de diagnóstico que te lleva rápidamente a la solución.
Empieza por el patrón de desconexión
Antes de cambiar ningún ajuste, observa cómo se producen las desconexiones. El momento en que ocurren dice mucho:
| Patrón | Causa más probable |
|---|---|
| Dos dispositivos se desconectan por turnos cada pocos segundos | Client ID duplicado |
| Desconexión a intervalos regulares, aproximadamente 1,5 x keep alive | No se está atendiendo el keep alive |
| Desconexión tras varios minutos de inactividad, solo en ciertas redes | Timeout de inactividad del NAT o del firewall |
| Desconexión justo después de publicar un mensaje concreto | Paquete demasiado grande o topic no válido |
| Desconexión cuando hay picos de tráfico | Límite de ritmo o cuota |
| Desconexión inmediatamente después de cada conexión | Error de autorización o de suscripción |
Averigua también qué lado cierra la conexión. Con MQTT 5, un cierre iniciado por el broker llega con un paquete DISCONNECT y un reason code. Con MQTT 3.1.1, el broker simplemente cierra el socket. Si puedes, cambia el cliente a MQTT 5 mientras depuras; el reason code a menudo responde a la pregunta al instante. La referencia de reason codes de MQTT los recoge todos.
Causa 1: client ID duplicados
La especificación MQTT establece que, si un cliente se conecta con un client ID que ya está conectado, el broker debe desconectar al cliente existente. Si ambos clientes tienen activada la reconexión automática, se expulsan mutuamente para siempre. En MQTT 5, el cliente que pierde recibe el reason code 0x8E Session taken over.
Esto ocurre más a menudo de lo que crees:
- Firmware con un client ID fijo en el código grabado en varios dispositivos.
- Un servicio escalado a dos réplicas con la misma configuración.
- Un desarrollador que ejecuta el mismo script en dos terminales.
- Una herramienta MQTT de escritorio que se quedó abierta con el ID que usa tu dispositivo.
Solución: deriva los client ID de algo único, como una dirección MAC, un número de serie o un nombre de host, y nunca compartas un ID entre procesos. La guía del client ID de MQTT cubre patrones de nomenclatura. En los brokers MQTT gestionados que emiten los client ID junto con las credenciales, usa una conexión por cada ID emitido.
Causa 2: timeouts de keep alive
El keep alive es una promesa del cliente: "tendrás noticias mías al menos cada N segundos". Si el broker no recibe nada durante 1,5 veces ese intervalo, cierra la conexión, con el motivo 0x8D Keep Alive timeout en MQTT 5. El cliente envía PINGREQ cuando no tiene nada más que enviar, pero solo si su bucle de red se está ejecutando.
Formas habituales de romperlo:
- Llamar a una función bloqueante larga, como
time.sleep(120)o una petición HTTP lenta, en el mismo hilo que ejecuta el bucle MQTT. - En Arduino, olvidarse de llamar a
mqtt.loop()con frecuencia o usardelay()durante minutos. - Fijar un keep alive muy corto, como 5 segundos, en un enlace móvil de alta latencia.
- Ciclos de deep sleep más largos que el intervalo de keep alive sin una desconexión limpia.
Solución: ejecuta el bucle de red en un hilo en segundo plano (loop_start() en Paho; MQTT.js lo hace automáticamente), mantén el trabajo bloqueante fuera de ese hilo y elige un keep alive de entre 30 y 120 segundos para la mayoría de los despliegues. La guía del intervalo de keep alive explica cómo ajustarlo.
Causa 3: timeouts de inactividad de NAT y firewalls
Las redes móviles, los routers domésticos y los firewalls corporativos hacen seguimiento de las conexiones TCP y las cortan en silencio tras un periodo de inactividad. No se avisa ni al cliente ni al broker. El cliente descubre el problema solo cuando intenta volver a enviar, y el broker solo cuando expira el keep alive.
La señal reveladora es que las desconexiones ocurren en la red móvil o en una ubicación concreta, pero no en tu red de desarrollo. Solución: fija un keep alive inferior al timeout de inactividad de la red, para que el tráfico de PINGREQ mantenga viva la asignación. Si un operador corta las conexiones inactivas al cabo de unos minutos, un keep alive de 60 segundos es un punto de partida razonable.
Causa 4: paquetes demasiado grandes o no válidos
Los brokers cierran la conexión cuando reciben un paquete que no pueden aceptar. Desencadenantes típicos:
- Payload por encima del tamaño máximo de mensaje del broker. Los clientes MQTT 5 ven
0x95 Packet too large; los clientes 3.1.1 simplemente pierden la conexión. Consulta la guía del límite de tamaño de mensaje MQTT. - Publicar en un topic que contiene
+o#. Los comodines solo son válidos en las suscripciones. - UTF-8 no válido en un nombre de topic, producido al concatenar cadenas con datos binarios.
- Mal uso de los alias de topic tras una reconexión en MQTT 5.
Solución: registra el topic y el tamaño del payload antes de cada publicación y comprueba si la desconexión sigue a un mensaje concreto. A menudo, el mismo mensaje demasiado grande se reintenta tras la reconexión, lo que crea un bucle.
Causa 5: límites de ritmo y cuotas
Los brokers compartidos y los brokers MQTT gestionados se protegen con límites. Publicar más rápido de lo permitido, abrir más conexiones de las que permite tu plan o superar un número de suscripciones puede provocar rechazos o desconexiones. En MQTT 5, busca 0x96 Message rate too high y 0x97 Quota exceeded.
En el servidor MQTT público gratuito de broker.connectmq.com, pensado para pruebas y prototipos y no para datos de producción, cada cliente está limitado a 10 mensajes por segundo, 10 suscripciones y 64 KB por mensaje. Los brokers privados tienen en cambio límites por plan; el plan Serverless gratuito, por ejemplo, permite hasta 25 conexiones y mensajes de 256 KB.
Un flujo de diagnóstico que funciona
- Activa los registros del cliente. En Paho,
client.enable_logger(); en las herramientas de Mosquitto,-d; en MQTT.js, defineDEBUG=mqttjs*. - Conéctate con MQTT 5 y registra el reason code y la Reason String de cada DISCONNECT.
- Anota las marcas de tiempo de conexiones y desconexiones y compara el intervalo con tu keep alive.
- Revisa el lado del broker. Mira el estado de conexión del client ID en el broker y si aparece otra conexión con el mismo ID en ese mismo momento.
- Reprodúcelo con un cliente mínimo. Ejecuta
mosquitto_subcon las mismas credenciales en la misma red. Si se mantiene conectado, el problema está en tu aplicación. - Cambia una sola cosa cada vez: client ID, keep alive, tamaño del payload, ritmo de publicación.
Este es un cliente Python mínimo que registra todo lo que necesitas para los pasos 1 a 3:
import time
import paho.mqtt.client as mqtt
started = time.time()
def ts():
return f"[{time.time() - started:7.1f}s]"
def on_connect(client, userdata, flags, reason_code, properties):
print(ts(), "CONNECT", reason_code, "session_present:", flags.session_present)
client.subscribe("connectmq/demo/5a3c/#", qos=1)
def on_disconnect(client, userdata, flags, reason_code, properties):
reason = getattr(properties, "ReasonString", "") if properties else ""
print(ts(), "DISCONNECT", reason_code, f"(0x{reason_code.value:02X})", reason)
client = mqtt.Client(
mqtt.CallbackAPIVersion.VERSION2,
client_id="disconnect-debug-5a3c",
protocol=mqtt.MQTTv5,
)
client.on_connect = on_connect
client.on_disconnect = on_disconnect
client.reconnect_delay_set(min_delay=1, max_delay=60)
client.connect("broker.connectmq.com", 1883, keepalive=60)
client.loop_forever(retry_first_connection=True)
Para ver desde otra terminal cómo percibe el broker a un cliente, suscríbete a su topic de estado de conexión:
mosquitto_sub -h broker.connectmq.com -p 8883 --capath /etc/ssl/certs \
-u YOUR_USERNAME -P YOUR_PASSWORD -i YOUR_MONITOR_CLIENT_ID \
-t '$SYS/broker/connection/YOUR_CLIENT_ID/state' -v
Pon el topic entre comillas simples para que el shell no expanda $SYS. La guía de los topics $SYS de MQTT explica qué más puedes monitorizar de esta forma. Como el broker vincula los client ID a las credenciales, la conexión de monitorización necesita su propio client ID válido; el cliente MQTT en el navegador del portal es una alternativa sencilla para echar un vistazo rápido.
Lógica de reconexión que no empeore las cosas
La reconexión automática es imprescindible, pero una implementación descuidada convierte un problema en muchos:
- Usa backoff exponencial con jitter para que una flota no se reconecte a la vez tras una caída.
- No reintentes ante errores de autorización. Los códigos
0x86y0x87no se arreglan solos, y los inicios de sesión fallidos repetidos pueden hacer que se bloquee tu IP. - Vuelve a suscribirte en
on_connect, no una sola vez al arrancar, salvo que dependas de una sesión persistente. - Usa un Last Will para que otros sistemas sepan cuándo se cae un dispositivo inesperadamente. Consulta MQTT Last Will and Testament.
Diagnostica las desconexiones con ConnectMQ
ConnectMQ te da una visibilidad del lado del broker de la que a menudo carecen las instalaciones autoalojadas. El panel del portal y la vista de dispositivos muestran qué clientes están conectados, el explorador de topics en directo muestra lo que publican y el topic $SYS/broker/connection/{clientId}/state permite que tus propias herramientas sigan los cambios de conexión. Los clientes MQTT 5 reciben reason codes ante infracciones de cuota y de protocolo, así que puedes ver por qué se produjo una desconexión en lugar de adivinarlo. Para conocer el contexto sobre qué hace un broker MQTT durante la vida de una conexión, consulta nuestro artículo explicativo, o el tutorial de MQTT para el protocolo completo.
Consigue conexiones estables con ConnectMQ
Un broker MQTT gestionado quita de tu lista la disponibilidad del broker para que puedas centrarte en el comportamiento de los clientes. Crea un broker Serverless gratuito en app.connectmq.com, sin tarjeta de crédito, y prueba tu lógica de reconexión con límites reales antes de lanzar. Compara opciones en nuestra guía del panorama de brokers MQTT en la nube.
Preguntas frecuentes
¿Por qué mi cliente MQTT se desconecta cada pocos segundos?
La causa más habitual es otro cliente que usa el mismo client ID. Cada conexión expulsa a la otra, y ambas se reconectan automáticamente. Da a cada cliente un ID único.
¿Qué valor de keep alive debo usar?
Entre 30 y 120 segundos es adecuado para la mayoría de los despliegues. Usa un valor inferior al timeout de inactividad de tu red y asegúrate de que el bucle de red del cliente se ejecuta de forma continua para que el PINGREQ se envíe realmente.
¿Cómo sé si cerró la conexión el broker o el cliente?
Conéctate con MQTT 5. Si la cerró el broker, el cliente recibe un paquete DISCONNECT con un reason code; si el socket simplemente se cayó, el cliente informa de un error de red sin reason code.
¿Puede un mensaje grande provocar desconexiones?
Sí. Si un paquete supera el tamaño máximo del broker, este cierra la conexión, y si el cliente reintenta el mismo mensaje tras reconectarse, vuelve a desconectarse. Comprueba el tamaño de los payloads frente al límite de tu broker.



