Eine MQTT-Session ist der Zustand, den Broker und Client übereinander speichern: welche Topics der Client abonniert hat und welche Nachrichten noch unterwegs sind. Bei einer persistenten Session übersteht dieser Zustand ein Disconnect – ein Gerät, das für ein paar Minuten aus dem Netz fällt, kehrt zu denselben Subscriptions zurück und erhält die Nachrichten, die es verpasst hat. Dieser Artikel erklärt, wie Sessions in MQTT 3.1.1 (cleanSession) und MQTT 5 (Clean Start plus Session Expiry Interval) funktionieren.
Was eine Session enthält
Die Spezifikation definiert den Session-Zustand auf beiden Seiten. Auf dem Broker gehören dazu:
- Die Existenz der Session selbst, identifiziert über die Client-ID.
- Die Subscriptions des Clients (Topic-Filter und ihre QoS).
- QoS-1- und QoS-2-Nachrichten, die an den Client gesendet, aber noch nicht vollständig bestätigt wurden.
- QoS-1- und QoS-2-Nachrichten, die darauf warten, an den Client gesendet zu werden – die „Offline-Queue“.
- QoS-2-Nachrichten, die vom Client empfangen, aber noch nicht vollständig bestätigt wurden.
- Optional QoS-0-Nachrichten, die auf den Versand warten (Broker dürfen diese puffern, tun es meist aber nicht).
Der Client speichert seine eigene Hälfte: QoS-1- und QoS-2-Nachrichten, die er gesendet hat, die aber noch nicht vollständig bestätigt sind, sowie empfangene QoS-2-Nachrichten, deren Ablauf noch nicht abgeschlossen ist. Deshalb halten die QoS-Stufen ihre Garantien über Reconnects hinweg nur dann ein, wenn eine persistente Session verwendet wird.
MQTT 3.1.1: das cleanSession-Flag
In MQTT 3.1.1 entscheidet ein einziges Bit im CONNECT-Paket über alles:
| cleanSession | Beim Verbinden | Beim Trennen |
|---|---|---|
1 (true) | Eine bestehende Session für diese Client-ID wird verworfen; eine neue beginnt | Die Session wird gelöscht |
0 (false) | Eine bestehende Session wird fortgesetzt; andernfalls wird eine neue angelegt | Die Session bleibt erhalten, und QoS-1/2-Nachrichten für ihre Subscriptions werden gepuffert |
Laut Protokoll läuft eine persistente Session in 3.1.1 nie von selbst ab. Broker ergänzen eigene Grenzen, etwa die Mosquitto-Einstellungen persistent_client_expiration und max_queued_messages – sonst würden sich verwaiste Client-IDs ewig ansammeln. Eine leere Client-ID ist nur mit cleanSession=1 erlaubt, weil sich eine Session, die nicht identifizierbar ist, auch nicht fortsetzen lässt.
MQTT 5: Clean Start und Session Expiry Interval
MQTT 5 teilt das alte Flag in zwei unabhängige Einstellungen auf:
- Clean Start (ein Flag) – ob eine bestehende Session beim Verbinden verworfen wird.
- Session Expiry Interval (eine
CONNECT-Property, in Sekunden) – wie lange der Broker die Session nach dem Schließen der Netzwerkverbindung aufbewahrt. Fehlt sie oder ist sie0, endet die Session mit der Verbindung;0xFFFFFFFFbedeutet, sie läuft nie ab.
Damit sind Kombinationen möglich, die 3.1.1 nicht ausdrücken konnte, etwa „immer frisch starten, aber meine Session eine Stunde behalten, falls ich wegfalle“ (Clean Start = 1, Expiry = 3600). Der Client kann die Expiry außerdem in seinem DISCONNECT-Paket ändern, sofern er sich nicht mit einer Expiry von 0 verbunden hat.
| MQTT 3.1.1 | Entsprechende MQTT-5-Einstellungen |
|---|---|
cleanSession = 1 | Clean Start = 1, Session Expiry Interval = 0 |
cleanSession = 0 | Clean Start = 0, Session Expiry Interval = 0xFFFFFFFF |
MQTT-5-Broker dürfen die angeforderte Expiry auch verkürzen und den tatsächlichen Wert über die Property Session Expiry Interval im CONNACK mitteilen. Kombiniere das mit dem nachrichtenbezogenen Message Expiry Interval, damit gepufferte Befehle nicht noch Stunden nach Ablauf ihrer Relevanz zugestellt werden. Weitere Unterschiede behandelt MQTT 5 vs. 3.1.1.
Das Session-Present-Flag
Das CONNACK des Brokers enthält ein Session Present-Flag, das dem Client mitteilt, was tatsächlich passiert ist:
1– der Broker hat eine gespeicherte Session gefunden und fortgesetzt. Die Subscriptions sind noch aktiv; du musst nicht erneut abonnieren.0– es wurde keine Session fortgesetzt (Clean Start war gesetzt, sie ist abgelaufen oder der Broker hat sie verloren). Der Client muss erneut abonnieren.
Robuste Clients prüfen dieses Flag, statt Annahmen zu treffen. Das sicherste einfache Muster: immer dann neu abonnieren, wenn Session Present 0 ist. Neu abonnieren bei 1 ist erlaubt, ersetzt aber die bestehenden Subscriptions und kann dazu führen, dass Retained Messages erneut gesendet werden.
Beispiele
In der Mosquitto-CLI deaktiviert -c die Clean Session und -i setzt eine feste Client-ID – beides ist für eine persistente Session nötig:
# 1. Create the session, then press Ctrl+C
mosquitto_sub -h broker.hivemq.com -i sensor-42-sub -c -q 1 -t 'testmqtt/sess-demo/#'
# 2. While it is offline, publish with QoS 1
mosquitto_pub -h broker.hivemq.com -q 1 -t 'testmqtt/sess-demo/cmd' -m 'reboot'
# 3. Reconnect with the same ID: the queued message is delivered
mosquitto_sub -h broker.hivemq.com -i sensor-42-sub -c -q 1 -t 'testmqtt/sess-demo/#'import mqtt from 'mqtt';
const client = mqtt.connect('wss://broker.hivemq.com:8884/mqtt', {
protocolVersion: 5,
clientId: 'sensor-42-sub',
clean: false, // Clean Start = 0
properties: { sessionExpiryInterval: 3600 },
});
client.on('connect', (connack) => {
if (!connack.sessionPresent) {
client.subscribe('testmqtt/sess-demo/#', { qos: 1 });
}
});import paho.mqtt.client as mqtt
def on_connect(client, userdata, flags, reason_code, properties):
if not flags.session_present:
client.subscribe("testmqtt/sess-demo/#", qos=1)
client = mqtt.Client(mqtt.CallbackAPIVersion.VERSION2,
client_id="sensor-42-sub", clean_session=False)
client.on_connect = on_connect
client.connect("broker.hivemq.com", 1883)
client.loop_forever()Entsprechenden Code für andere Sprachen erzeugst du mit dem MQTT-Code-Generator, und mit dem Client-ID-Generator erstellst du stabile, eindeutige IDs. Um die Testnachrichten aus dem Browser zu publishen, nutze den Online-Client:
Im Online-MQTT-Client ausprobieren
Wie lange sollte eine Session leben?
Mit MQTT 5 musst du ein Session Expiry Interval wählen. Der richtige Wert hängt davon ab, wie lange der Client realistischerweise weg sein kann und sich dann noch für das Verpasste interessiert:
- Mobile Apps und Dashboards – Minuten. Lang genug, um einen Tunnel oder einen Netzwechsel zu überstehen, kurz genug, damit der Broker keinen Zustand für Nutzer hält, die die App geschlossen haben.
- Feldgeräte an unzuverlässigen Verbindungen – Stunden bis ein Tag, abgestimmt auf typische Ausfallzeiten.
- Backend-Consumer – lang genug, um ein Deployment- oder Neustartfenster abzudecken, damit keine Nachricht verloren geht, während eine neue Version startet.
Sessions wirken außerdem mit dem Last Will zusammen. In MQTT 5 wird der Will publisht, wenn das Will Delay Interval abläuft oder die Session endet – je nachdem, was zuerst eintritt. Ein kurzer Will Delay bei einer längeren Session lässt ein Gerät nach einer kurzen Störung still wieder verbinden, ohne einen „offline“-Alarm auszulösen.
Häufige Stolperfallen
- Zufällige Client-IDs. Viele Bibliotheken erzeugen bei jedem Start eine zufällige ID und legen so stillschweigend jedes Mal eine neue Session an. Persistente Sessions brauchen eine stabile ID.
- QoS 0 irgendwo in der Kette. Die zugestellte QoS ist die niedrigere von Publish- und Subscription-QoS. Ist eine davon 0, werden Nachrichten für Offline-Clients nicht gepuffert.
- Zwei Clients mit derselben ID. Der Broker trennt den älteren, sobald sich der neuere verbindet, und beide kämpfen um die Session. Siehe MQTT-Verbindungsfehler.
- Unbegrenzte Queues. Ein Gerät, das tagelang offline ist, kann einen großen Rückstau ansammeln. Nutze Session- und Message-Expiry sowie Queue-Limits des Brokers, um das im Griff zu behalten.
- Broker-Neustarts. Sessions überstehen einen Broker-Neustart nur, wenn der Broker sie auf die Festplatte schreibt (bei Mosquitto
persistence true).
Wann du persistente Sessions einsetzen solltest
Setze sie für Geräte ein, die keine Befehle oder Konfigurationsupdates verpassen dürfen, und für Backend-Consumer, die jede QoS-1-Nachricht verarbeiten müssen. Clean Sessions sind die bessere Wahl für kurzlebige Tools, für Dashboards, die nur die neuesten Werte brauchen (das decken Retained Messages ab), und für alles, was sich mit Wegwerf-Client-IDs verbindet. In wackeligen Netzen kombinierst du eine persistente Session mit einem sinnvollen Keep-Alive-Intervall, damit Verbindungsabbrüche schnell erkannt werden und die Queue übernimmt.
Häufig gestellte Fragen
Was bedeutet clean session false in MQTT?
Mit cleanSession = false in MQTT 3.1.1 behält der Broker die Session des Clients nach dem Trennen: seine Subscriptions und alle QoS-1- und QoS-2-Nachrichten, die eintreffen, während er offline ist. Verbindet sich der Client mit genau derselben Client-ID neu, setzt er diese Session fort und erhält die gepufferten Nachrichten.
Warum erhalte ich trotz persistenter Session keine Offline-Nachrichten?
Prüfe vier Dinge: Der Client verbindet sich mit exakt derselben Client-ID neu, Clean Session ist false (bzw. in MQTT 5 Clean Start false mit einem Session Expiry Interval ungleich null), Subscription und Publish nutzen beide QoS 1 oder 2, und der Broker hat weder sein Queue-Limit noch die Session Expiry erreicht.
Was ist der Unterschied zwischen Clean Session und Clean Start?
In MQTT 3.1.1 steuert cleanSession sowohl, ob eine alte Session beim Verbinden verworfen wird, als auch, ob die neue das Trennen überdauert. MQTT 5 trennt das: Clean Start steuert nur das Verwerfen der alten Session, und das Session Expiry Interval legt fest, wie lange die Session nach dem Trennen bestehen bleibt.