TCP-Verbindungen können still sterben. Ein Router startet neu, ein Smartphone wechselt die Funkzelle, ein NAT-Tabelleneintrag läuft ab – und keine Seite bekommt ein sauberes Schließen mit. Der Keep-Alive-Mechanismus von MQTT sorgt dafür, dass Client und Broker eine tote Verbindung innerhalb einer vorhersehbaren Zeit bemerken und der Broker in diesem Fall die Last-Will-Nachricht des Clients publishen kann. Dieser Artikel erklärt genau, wie Keep-Alive in MQTT 3.1.1 und 5 funktioniert und wie du einen Wert wählst, der zu deinem Netzwerk passt.
Was das Keep-Alive-Intervall ist
Der Keep-Alive ist ein 16-Bit-Wert in Sekunden, den der Client in seinem CONNECT-Paket mitschickt. Er ist ein Versprechen: „Ich werde nie länger als diese Zeit schweigen.“ Der Höchstwert liegt bei 65535 Sekunden (etwas über 18 Stunden), und 0 schaltet den Mechanismus komplett ab.
Entscheidend ist: Der Timer bezieht sich auf jedes Control Packet, nicht nur auf Pings. Ein Client, der bei 60 Sekunden Keep-Alive alle 10 Sekunden einen Sensorwert publisht, muss nie pingen, weil die PUBLISH-Pakete bereits beweisen, dass er lebt. Pings werden nur gesendet, wenn der Client sonst nichts zu sagen hat.
PINGREQ und PINGRESP
Wenn die Keep-Alive-Zeit abzulaufen droht, ohne dass der Client etwas gesendet hat, schickt er ein PINGREQ. Der Broker muss mit einem PINGRESP antworten. Beide Pakete sind winzig: nur ein 2-Byte-Fixed-Header ohne Variable Header und ohne Payload.
| Paket | Richtung | Bytes auf der Leitung | Zweck |
|---|---|---|---|
PINGREQ | Client → Broker | 0xC0 0x00 | Beweist, dass der Client lebt; fragt, ob der Broker lebt |
PINGRESP | Broker → Client | 0xD0 0x00 | Beweist, dass der Broker und der Rückweg zum Client funktionieren |
Zwei Bytes MQTT sind nicht die ganze Wahrheit – TCP/IP, TLS-Records und bei Browsern das WebSocket-Framing fügen jedem Ping Overhead hinzu. Wenn du einen Mobilfunk-Datentarif dimensionierst, hilft dir der MQTT-Paketgrößen-Rechner, den MQTT-Anteil jedes Pakets abzuschätzen, sodass du den Transport-Overhead obendrauf rechnen kannst.
Die 1,5×-Regel: Wann eine Verbindung als tot gilt
Beide Seiten nutzen den Keep-Alive unterschiedlich:
- Broker-Seite – erhält der Broker innerhalb des Anderthalbfachen der Keep-Alive-Zeit kein Control Packet vom Client, muss er die Netzwerkverbindung schließen, als wäre das Netz ausgefallen. Bei 60 Sekunden Keep-Alive sind das 90 Sekunden Stille.
- Client-Seite – sendet der Client ein
PINGREQund erhält nicht innerhalb einer angemessenen Zeit einPINGRESP, sollte er die Verbindung selbst schließen. Bibliotheken werten eine fehlende Ping-Antwort meist beim nächsten Keep-Alive-Tick als Fehler und starten einen Reconnect.
Da der Broker einen Keep-Alive-Timeout als unsauberes Disconnect behandelt, wird der Last Will des Clients publisht. Genau das macht das klassische „Gerät offline“-Muster möglich: Setze einen Retained Will mit offline auf devices/42/status, und der Broker meldet den Ausfall in deinem Namen spätestens 1,5 Keep-Alive-Perioden, nachdem das Gerät verschwunden ist.
Server Keep Alive in MQTT 5
In MQTT 3.1.1 legt der Client den Keep-Alive fest, und der Broker muss ihn akzeptieren (oder die Verbindung ablehnen). MQTT 5 ergänzt das CONNACK um die Property Server Keep Alive. Sendet der Broker sie mit, muss der Client den Wert des Brokers statt des angefragten verwenden. Broker nutzen das, um zu verhindern, dass Clients einen Keep-Alive von 0 oder vielen Stunden anfordern, wodurch sich tote Verbindungen ansammeln würden.
MQTT 5 macht den Fehler außerdem explizit: Vor dem Schließen kann ein Broker ein DISCONNECT mit dem Reason Code 0x8D (Keep Alive timeout) senden. Diesen und alle anderen Codes kannst du in der Referenz der MQTT-Reason-Codes nachschlagen. Die übrigen Protokollunterschiede findest du unter MQTT 5 vs. 3.1.1.
Broker setzen oft auch serverseitig Grenzen. Mosquitto hat zum Beispiel die Option max_keepalive: MQTT-5-Clients bekommen den gekappten Wert über Server Keep Alive mitgeteilt, während MQTT-3.1.1-Clients, die mehr anfordern, abgelehnt werden können.
Keep-Alive in gängigen Clients einstellen
Die meisten Bibliotheken verwenden standardmäßig 60 Sekunden. So änderst du den Wert:
# -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()Du willst das komplette Snippet für deine Sprache? Der MQTT-Code-Generator erzeugt Verbindungscode mit bereits gesetztem Keep-Alive. Um das Verhalten live zu beobachten, verbinde den Online-MQTT-Client mit einem kurzen Keep-Alive und lass ihn untätig.
Im Online-MQTT-Client ausprobieren
NAT, Firewalls und Mobilfunknetze
Keep-Alive hat eine zweite Aufgabe, die in der Spezifikation nicht erwähnt wird: Middleboxen bei Laune halten. NAT-Gateways, Carrier-Grade-NAT in Mobilfunknetzen, Load Balancer und Stateful Firewalls verwerfen inaktive TCP-Flows nach einem Timeout. Ist das Mapping weg, glaubt der Client weiterhin, verbunden zu sein – aber nichts, was er sendet, erreicht den Broker, und nichts vom Broker erreicht den Client.
- Bleib unter dem kürzesten Idle-Timeout auf der Strecke. Cloud-Load-Balancer und Mobilfunkanbieter nutzen oft Idle-Timeouts von wenigen Minuten, manchmal weniger. Ein Keep-Alive deutlich darunter hält das Mapping am Leben.
- Verlass dich nicht auf TCP-Keepalive. Die TCP-Keepalive-Standardwerte des Betriebssystems liegen meist im Stundenbereich – viel zu langsam, um eine MQTT-Session zu schützen.
- Wäge gegen den Akku ab. Jeder Ping weckt das Funkmodul. Bei akkubetriebenen Mobilfunkgeräten sind längere Intervalle in Kombination mit einer persistenten Session (damit verpasste Nachrichten gepuffert werden) oft der bessere Kompromiss.
- WebSockets sind keine Ausnahme. Browser-Clients hinter Proxys haben dieselben Idle-Timeouts; MQTT-Keep-Alive funktioniert bei MQTT über WebSockets genauso.
So wählst du einen Keep-Alive-Wert
| Szenario | Empfohlener Keep-Alive | Warum |
|---|---|---|
| Dashboards und Backend-Services in stabilen Netzen | 30–60 s | Bibliotheks-Standard, geringer Overhead, Erkennung in etwa einer Minute |
| Geräte, bei denen „offline“ schnell sichtbar sein muss | 10–20 s | Die Will-Nachricht wird nach etwa 15–30 s ausgelöst |
| Mobilfunk- / NAT-lastige Netze | Unter dem kürzesten Idle-Timeout auf der Strecke | Verhindert stille halboffene Verbindungen |
| Akkubetrieben, seltene Meldungen | Mehrere Minuten oder Connect–Publish–Disconnect | Weniger Funk-Aufwachvorgänge; auf persistente Sessions setzen |
Keep-Alive-Disconnects beheben
Trennen sich Clients alle 1,5 Keep-Alive-Perioden, liegt das meist an einer Client-Loop, die nicht läuft – zum Beispiel ist die Netzwerk-Loop von Paho durch langwierige Arbeit in einem Callback blockiert, sodass kein PINGREQ gesendet wird. Häufige Reconnects in einem festen Abstand von einigen Minuten deuten dagegen auf einen Idle-Timeout in einem Proxy oder NAT hin. Weitere Ursachen findest du im Guide zu MQTT-Verbindungsfehlern.
Häufig gestellte Fragen
Was ist ein gutes MQTT-Keep-Alive-Intervall?
Für die meisten Geräte in stabilen Netzen sind 30 bis 60 Sekunden ein sinnvoller Standard. Nimm kürzere Werte (15 bis 30 Sekunden), wenn du tote Clients schnell erkennen oder unter einem aggressiven NAT-Timeout bleiben musst, und längere Werte bei akkubetriebenen Geräten, bei denen jedes Aufwachen des Funkmoduls Energie kostet.
Was passiert, wenn der MQTT-Keep-Alive abläuft?
Erhält der Broker innerhalb des Anderthalbfachen des Keep-Alive-Intervalls kein Control Packet vom Client, schließt er die Netzwerkverbindung und wertet sie als unsauberes Disconnect. Das heißt, die Last-Will-Nachricht wird – falls vorhanden – publisht. In MQTT 5 kann der Broker vorher ein DISCONNECT mit Reason Code 0x8D (Keep Alive timeout) senden.
Deaktiviert ein Keep-Alive von 0 den Mechanismus?
Ja. Ein Keep-Alive von 0 schaltet den Mechanismus ab, sodass der Broker den Client nie wegen Inaktivität trennt. Das ist selten eine gute Idee, weil halboffene TCP-Verbindungen dann sehr lange bestehen bleiben können, ohne dass es jemand merkt.