Fondamentaux MQTT

Pourquoi votre client MQTT se déconnecte sans cesse (et comment y remédier)

Les boucles de reconnexion sont l'un des problèmes MQTT les plus courants. Découvrez les cinq causes habituelles, des client ID en double aux timeouts de keep alive et aux paquets trop volumineux, et suivez une méthode de diagnostic qui identifie la vôtre.

Graphique d'un appareil qui se déconnecte et se reconnecte à répétition pour montrer pourquoi un client MQTT se déconnecte sans cesse
Essayez-le sur votre propre broker MQTTObtenez un broker MQTT privé et managé avec l’offre Free et partez d’exemples de code prêts à l’emploi.
Essayer gratuitement Exemples de code Aucune carte bancaire requise

Si votre client MQTT se déconnecte sans cesse, la cause est presque toujours l'une de ces cinq : deux clients qui partagent un client ID, un timeout de keep alive, un réseau qui coupe silencieusement les connexions inactives, un paquet que le broker refuse, ou une limite d'offre ou de débit. Ce guide explique comment reconnaître chacune d'elles à partir des symptômes et des journaux, et propose une méthode de diagnostic qui vous mène rapidement à la solution.

Commencez par le schéma des déconnexions

Avant de modifier le moindre paramètre, observez comment surviennent les déconnexions. Leur rythme en dit long :

Schéma Cause la plus probable
Deux appareils se déconnectent à tour de rôle toutes les quelques secondes Client ID en double
Déconnexion à intervalle régulier, environ 1,5 x keep alive Keep alive non assuré
Déconnexion après plusieurs minutes d'inactivité, uniquement sur certains réseaux Timeout d'inactivité NAT ou pare-feu
Déconnexion juste après la publication d'un message particulier Paquet trop gros ou topic invalide
Déconnexion lors des pics de trafic Limite de débit ou quota
Déconnexion immédiatement après chaque connexion Erreur d'autorisation ou d'abonnement

Déterminez aussi quel côté ferme la connexion. En MQTT 5, une fermeture à l'initiative du broker s'accompagne d'un paquet DISCONNECT et d'un reason code. En MQTT 3.1.1, le broker ferme simplement le socket. Si possible, passez le client en MQTT 5 pendant le débogage : le reason code répond souvent instantanément à la question. La référence des reason codes MQTT les répertorie tous.

Cause 1 : client ID en double

La spécification MQTT indique que si un client se connecte avec un client ID déjà connecté, le broker doit déconnecter le client existant. Si les deux clients ont la reconnexion automatique activée, ils s'éjectent mutuellement à l'infini. En MQTT 5, le client évincé reçoit le reason code 0x8E Session taken over.

Cela arrive plus souvent qu'on ne le pense :

  • Un firmware avec un client ID codé en dur flashé sur plusieurs appareils.
  • Un service mis à l'échelle sur deux réplicas avec la même configuration.
  • Un développeur qui exécute le même script dans deux terminaux.
  • Un outil MQTT de bureau resté ouvert avec l'ID qu'utilise votre appareil.

Solution : dérivez les client ID de quelque chose d'unique, comme une adresse MAC, un numéro de série ou un nom d'hôte, et ne partagez jamais un ID entre processus. Le guide du client ID MQTT présente des modèles de nommage. Sur les brokers MQTT managés qui émettent les client ID avec les identifiants, utilisez une seule connexion par ID émis.

Cause 2 : timeouts de keep alive

Le keep alive est une promesse du client : « vous aurez de mes nouvelles au moins toutes les N secondes ». Si le broker ne reçoit rien pendant 1,5 fois cet intervalle, il ferme la connexion, avec la raison 0x8D Keep Alive timeout en MQTT 5. Le client envoie un PINGREQ lorsqu'il n'a rien d'autre à envoyer, mais seulement si sa boucle réseau tourne.

Façons courantes de le casser :

  • Appeler une longue fonction bloquante, comme time.sleep(120) ou une requête HTTP lente, dans le thread qui exécute la boucle MQTT.
  • Sur Arduino, oublier d'appeler fréquemment mqtt.loop(), ou utiliser delay() pendant plusieurs minutes.
  • Définir un keep alive très court, comme 5 secondes, sur un lien cellulaire à forte latence.
  • Des cycles de deep sleep plus longs que l'intervalle de keep alive sans déconnexion propre.

Solution : exécutez la boucle réseau dans un thread d'arrière-plan (loop_start() dans Paho, ce que MQTT.js fait automatiquement), gardez les traitements bloquants hors de ce thread et choisissez un keep alive de 30 à 120 secondes pour la plupart des déploiements. Le guide de l'intervalle keep alive explique comment le régler.

Cause 3 : timeouts d'inactivité NAT et pare-feu

Les réseaux mobiles, les box domestiques et les pare-feu d'entreprise suivent les connexions TCP et les coupent silencieusement après une période d'inactivité. Ni le client ni le broker n'en sont informés. Le client ne découvre le problème qu'à sa prochaine tentative d'envoi, et le broker qu'à l'expiration du keep alive.

Le signe révélateur : les déconnexions surviennent en cellulaire ou sur un site précis, mais pas sur votre réseau de développement. Solution : définissez un keep alive inférieur au timeout d'inactivité du réseau, pour que le trafic PINGREQ maintienne la translation active. Si un opérateur coupe les connexions inactives après quelques minutes, un keep alive de 60 secondes est un point de départ raisonnable.

Cause 4 : paquets trop volumineux ou invalides

Les brokers ferment la connexion lorsqu'ils reçoivent un paquet qu'ils ne peuvent pas accepter. Déclencheurs typiques :

  • Payload supérieur à la taille maximale de message du broker. Les clients MQTT 5 voient 0x95 Packet too large ; les clients 3.1.1 perdent simplement la connexion. Consultez le guide de la taille maximale des messages MQTT.
  • Publication sur un topic contenant + ou #. Les wildcards ne sont valides que dans les abonnements.
  • UTF-8 invalide dans un nom de topic, produit par la concaténation de chaînes avec des données binaires.
  • Mauvais usage des topic aliases après une reconnexion en MQTT 5.

Solution : journalisez le topic et la taille du payload avant chaque publication, et vérifiez si la déconnexion suit un message précis. Le même message trop volumineux est souvent renvoyé après la reconnexion, ce qui crée une boucle.

Cause 5 : limites de débit et quotas

Les brokers partagés et managés se protègent par des limites. Publier plus vite que permis, ouvrir plus de connexions que votre offre ne l'autorise ou dépasser un nombre d'abonnements peut provoquer des refus ou des déconnexions. En MQTT 5, recherchez 0x96 Message rate too high et 0x97 Quota exceeded.

Sur le serveur MQTT public gratuit broker.connectmq.com, réservé aux tests, chaque client est limité à 10 messages par seconde, 10 abonnements et 64 Ko par message. Les brokers privés ont plutôt des limites par offre ; l'offre Serverless gratuite, par exemple, autorise jusqu'à 25 connexions et des messages de 256 Ko.

Une méthode de diagnostic qui fonctionne

  1. Activez la journalisation du client. Dans Paho, client.enable_logger() ; dans les outils Mosquitto, -d ; dans MQTT.js, définissez DEBUG=mqttjs*.
  2. Connectez-vous en MQTT 5 et journalisez le reason code et la Reason String de chaque DISCONNECT.
  3. Enregistrez l'horodatage des connexions et déconnexions, et comparez l'intervalle avec votre keep alive.
  4. Vérifiez côté broker. Regardez l'état de connexion du client ID sur le broker et si une autre connexion avec le même ID apparaît au même moment.
  5. Reproduisez avec un client minimal. Lancez mosquitto_sub avec les mêmes identifiants sur le même réseau. S'il reste connecté, le problème vient de votre application.
  6. Ne changez qu'une chose à la fois : client ID, keep alive, taille du payload, rythme de publication.

Voici un client Python minimal qui journalise tout ce dont vous avez besoin pour les étapes 1 à 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)

Pour suivre depuis un autre terminal la vision qu'a le broker d'un client, abonnez-vous à son topic d'état de connexion :

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

Entourez le topic d'apostrophes simples pour que le shell n'interprète pas $SYS. Le guide des topics $SYS MQTT explique ce que vous pouvez surveiller d'autre de cette façon. Comme le broker lie les client ID aux identifiants, la connexion de supervision a besoin de son propre client ID valide ; le client MQTT intégré au navigateur du portail est une alternative simple pour un coup d'œil rapide.

Une logique de reconnexion qui n'aggrave pas les choses

La reconnexion automatique est indispensable, mais une implémentation négligée transforme un problème en plusieurs :

  • Utilisez un backoff exponentiel avec jitter pour qu'une flotte ne se reconnecte pas en bloc après une panne.
  • Ne réessayez pas sur les erreurs d'autorisation. Les codes 0x86 et 0x87 ne se résoudront pas d'eux-mêmes, et des échecs de connexion répétés peuvent faire bloquer votre IP.
  • Réabonnez-vous dans on_connect, et pas une seule fois au démarrage, sauf si vous comptez sur une session persistante.
  • Utilisez un Last Will pour que les autres systèmes sachent quand un appareil décroche de façon inattendue. Consultez MQTT Last Will and Testament.

Diagnostiquer les déconnexions avec ConnectMQ

ConnectMQ vous offre une visibilité côté broker qui manque souvent aux installations auto-hébergées. Le tableau de bord du portail et la vue des appareils montrent quels clients sont connectés, l'explorateur de topics en direct montre ce qu'ils publient, et le topic $SYS/broker/connection/{clientId}/state permet à vos propres outils de suivre les changements de connexion. Les clients MQTT 5 reçoivent des reason codes pour les violations de quota et de protocole : vous voyez ainsi pourquoi une déconnexion s'est produite au lieu de deviner. Pour comprendre ce que fait un broker MQTT tout au long de la vie d'une connexion, consultez notre article explicatif, ou le tutoriel MQTT pour le protocole complet.

Des connexions stables avec ConnectMQ

Un broker MQTT managé vous décharge de la disponibilité du broker pour que vous puissiez vous concentrer sur le comportement des clients. Créez un broker Serverless gratuit sur app.connectmq.com, sans carte bancaire, et testez votre logique de reconnexion face à de vraies limites avant la mise en production. Comparez les options dans notre guide du paysage des brokers MQTT cloud.

Questions fréquentes

Pourquoi mon client MQTT se déconnecte-t-il toutes les quelques secondes ?

La cause la plus courante est un autre client qui utilise le même client ID. Chaque connexion éjecte l'autre, et les deux se reconnectent automatiquement. Donnez à chaque client un ID unique.

Quelle valeur de keep alive utiliser ?

Entre 30 et 120 secondes convient à la plupart des déploiements. Choisissez une valeur inférieure au timeout d'inactivité de votre réseau, et assurez-vous que la boucle réseau du client tourne en continu pour que le PINGREQ soit réellement envoyé.

Comment savoir si c'est le broker ou le client qui a fermé la connexion ?

Connectez-vous en MQTT 5. Si le broker l'a fermée, le client reçoit un paquet DISCONNECT avec un reason code ; si le socket est simplement tombé, le client signale une erreur réseau sans reason code.

Un message volumineux peut-il provoquer des déconnexions ?

Oui. Si un paquet dépasse la taille maximale du broker, celui-ci ferme la connexion, et si le client renvoie le même message après s'être reconnecté, il est de nouveau déconnecté. Comparez la taille de vos payloads à la limite de votre broker.

Articles liés

Qui a inventé MQTT ? L'histoire, des oléoducs à l'IoT

MQTT a été inventé en 1999 par Andy Stanford-Clark (IBM) et Arlen Nipper (Arcom) pour surveiller des oléoducs par satellite. Suivez son parcours vers l'open source, OASIS, l'ISO et les brokers MQTT managés d'aujourd'hui.

8 min de lecture

Taille maximale d'un message MQTT : limites et stratégies pour les gros payloads

MQTT peut techniquement transporter un paquet de 256 Mo, mais les brokers, les bibliothèques clientes et les appareils imposent des limites bien plus basses. Découvrez où se trouvent les vraies limites, ce qui se passe quand on les dépasse et comment envoyer de gros payloads.

8 min de lecture

40 questions d'entretien MQTT et leurs réponses pour développeurs IoT

Quarante questions d'entretien MQTT avec des réponses concises sur les fondamentaux, la QoS, les sessions, les retained messages, le Last Will, MQTT 5 et la sécurité, ainsi que des moyens de mettre en pratique chaque concept sur broker.connectmq.com.

10 min de lecture

Tous les articles →

Votre broker MQTT privé, à quelques clics

Créez un compte, récupérez les identifiants de votre broker et connectez votre premier appareil — aucun serveur à installer, mettre à jour ou dimensionner.