Die MQTT-Paketstruktur ist bewusst kompakt: Jedes Paket beginnt mit einem Fixed Header von nur zwei Byte, gefolgt von einem optionalen Variable Header und einem optionalen Payload. Dieser Rundgang dekodiert echte CONNECT-, CONNACK- und PUBLISH-Pakete Byte für Byte, damit Sie einen Wireshark-Mitschnitt oder einen Hex-Dump sicher lesen und Kodierungsfehler in Ihren eigenen Clients erkennen können.
Die drei Teile jedes MQTT-Pakets
Jedes MQTT-Control-Paket folgt demselben Aufbau:
| Teil | Vorhanden in | Inhalt |
|---|---|---|
| Fixed Header | Allen Paketen | Pakettyp, Flags, Remaining Length |
| Variable Header | Den meisten Paketen | Paketspezifische Felder wie Topic-Name, Packet Identifier, Protokollname und in MQTT 5 Properties |
| Payload | Einigen Paketen | Client ID und Zugangsdaten im CONNECT, Anwendungsnachricht im PUBLISH, Topic-Filter im SUBSCRIBE |
Die kleinsten Pakete, PINGREQ, PINGRESP und ein DISCONNECT in MQTT 3.1.1, bestehen nur aus dem zwei Byte langen Fixed Header. Diese Sparsamkeit ist ein wesentlicher Grund, warum MQTT über langsame Verbindungen gut funktioniert, und deshalb hilft das Verständnis der Struktur, wenn Sie die MQTT-Bandbreitennutzung abschätzen.
Der Fixed Header
Das erste Byte teilt sich in zwei Hälften:
Bit: 7 6 5 4 | 3 2 1 0
packet type | flags
Die oberen vier Bits enthalten den Pakettyp, von 1 (CONNECT) bis 15 (AUTH). Die unteren vier Bits sind Flags. Bei den meisten Paketen sind die Flags feste, reservierte Werte; nur PUBLISH nutzt sie inhaltlich:
| Bit | Bedeutung bei PUBLISH |
|---|---|
| 3 | DUP: erneute Zustellung einer QoS-1- oder QoS-2-Nachricht |
| 2-1 | QoS-Stufe: 00, 01 oder 10 |
| 0 | RETAIN: Broker soll diese Nachricht speichern |
Ein erstes Byte 0x30 ist also ein PUBLISH mit QoS 0, 0x32 eines mit QoS 1, 0x31 eine retained QoS-0-Nachricht und 0x3A eine erneute QoS-1-Zustellung mit gesetztem DUP. SUBSCRIBE, UNSUBSCRIBE und PUBREL müssen die Flags 0010 haben, weshalb sie als 0x82, 0xA2 und 0x62 erscheinen. Die Referenz der MQTT-Control-Pakete listet jeden Typ mit seinem ersten Byte auf.
Kodierung der Remaining Length
Nach dem ersten Byte folgt die Remaining Length: die Anzahl der Bytes von Variable Header plus Payload, ohne den Fixed Header selbst. Sie verwendet einen Variable Byte Integer von einem bis vier Byte. Jedes Byte trägt sieben Bit Nutzwert, und das oberste Bit signalisiert „es folgt ein weiteres Byte“.
| Wertebereich | Verwendete Bytes |
|---|---|
| 0 bis 127 | 1 |
| 128 bis 16.383 | 2 |
| 16.384 bis 2.097.151 | 3 |
| 2.097.152 bis 268.435.455 | 4 |
Beispiel: eine Remaining Length von 321. Da 321 = 2 x 128 + 65 ist, lautet das erste Byte 65 mit gesetztem Fortsetzungsbit (0xC1) und das zweite Byte 2 (0x02). Aus dem Maximum von vier Byte ergibt sich die oft zitierte Paketobergrenze des Protokolls von 256 MB; der Leitfaden zur maximalen MQTT-Nachrichtengröße erklärt, warum reale Broker deutlich niedrigere Limits setzen.
Hier ein Decoder, den Sie beim Lesen von Hex-Dumps verwenden können:
def decode_remaining_length(data: bytes, offset: int = 1):
multiplier, value = 1, 0
for i in range(4):
byte = data[offset + i]
value += (byte & 0x7F) * multiplier
if byte & 0x80 == 0:
return value, i + 1 # Wert, Anzahl verwendeter Bytes
multiplier *= 128
raise ValueError("Malformed remaining length")
print(decode_remaining_length(bytes([0x30, 0xC1, 0x02]))) # (321, 2)
Eine echte Session Schritt für Schritt
Führen Sie diesen Befehl aus, um eine Nachricht per MQTT 3.1.1, das mosquitto_pub standardmäßig verwendet, an den öffentlichen MQTT-Broker zu senden, der für Tests und Prototypen gedacht ist:
mosquitto_pub -h broker.connectmq.com -p 1883 -i demo-a1b2 \
-t "connectmq/demo/a1b2/t" -m "21.5" -d
Port 1883 ist unverschlüsselt, ein Paketmitschnitt zeigt also jedes Byte. Zeichnen Sie in Wireshark auf Ihrer Netzwerkschnittstelle auf und wenden Sie den Anzeigefilter mqtt an. Wireshark dekodiert Port 1883 automatisch als MQTT; Sie sehen CONNECT, CONNACK, PUBLISH und DISCONNECT. Verwenden Sie ein eigenes zufälliges Topic-Segment und denken Sie daran, dass alles auf dem öffentlichen MQTT-Broker für andere lesbar ist.
CONNECT
Das erste Paket des Clients umfasst 23 Byte:
10 15 Fixed Header: Typ 1 (CONNECT), Remaining Length 21
00 04 4D 51 54 54 Protokollname: Länge 4, "MQTT"
04 Protokoll-Level: 4 = MQTT 3.1.1
02 Connect Flags: Clean Session
00 3C Keep Alive: 60 Sekunden
00 09 64 65 6D 6F 2D 61 31 62 32 Payload: Client-ID-Länge 9, "demo-a1b2"
Das Byte der Connect Flags verdient einen genaueren Blick. Bit 1 ist Clean Session, Bit 2 Will Flag, Bits 3-4 Will QoS, Bit 5 Will Retain, Bit 6 Password Flag und Bit 7 User Name Flag. Ein Client, der sich mit Benutzername, Passwort und Clean Session verbindet, würde 0xC2 senden. Sind diese Flags gesetzt, folgen Benutzername und Passwort im Payload auf die Client ID, jeweils mit einem zwei Byte langen Längenpräfix. Das ist ein Grund, mit echten Zugangsdaten immer TLS auf Port 8883 zu verwenden: Auf Port 1883 werden sie im Klartext übertragen.
CONNACK
Der Broker antwortet mit vier Byte:
20 02 00 00
Typ 2, Remaining Length 2, Session Present 0, Rückgabecode 0 (angenommen). Jedes andere letzte Byte ist eine Ablehnung; der Leitfaden zu den MQTT-CONNACK-Rückgabecodes dekodiert jeden Wert.
PUBLISH
Die Nachricht selbst umfasst 29 Byte:
30 1B Fixed Header: PUBLISH, QoS 0, kein Retain; Remaining Length 27
00 15 Topic-Länge: 21
63 6F 6E 6E 65 63 74 6D 71 2F 64 65 6D 6F 2F 61 31 62 32 2F 74
Topic: "connectmq/demo/a1b2/t"
32 31 2E 35 Payload: "21.5"
Beachten Sie, was fehlt: Es gibt keinen Packet Identifier, weil QoS-0-Nachrichten nie bestätigt werden. Mit -q 1 wird das erste Byte zu 0x32, nach dem Topic erscheinen zwei Byte Packet Identifier, und der Broker antwortet mit einem vier Byte langen PUBACK (40 02 plus Identifier). Es gibt auch kein Feld für die Payload-Länge; der Payload ist einfach alles, was laut Remaining Length nach dem Variable Header übrig bleibt.
Das Beispiel zeigt auch, wie viel der Topic-Name kostet: 21 Byte Topic stehen 4 Byte Payload gegenüber. In Industrie-4.0-Anlagen mit einem Unified Namespace (UNS) nach ISA-95 sind Topics wie acme/werk-stuttgart/montage/linie-1/zelle-3/temperatur noch länger, sodass der Topic-Name bei kleinen Messwerten oft den größten Teil des Pakets ausmacht. Topic Aliases aus MQTT 5 setzen genau hier an.
DISCONNECT
Zum Schluss sendet der Client E0 00: Typ 14, Remaining Length 0. In MQTT 3.1.1 ist das das gesamte Paket.
Was sich in MQTT 5 ändert
MQTT 5 behält dasselbe Grundgerüst bei, ergänzt aber den Variable Header der meisten Pakete um Properties. Ein Properties-Abschnitt beginnt mit einer eigenen Länge als Variable Byte Integer, gefolgt von Paaren aus Kennung und Wert. Selbst ohne Properties ist das Längenbyte 00 vorhanden.
Für dieselbe Session über MQTT 5 gilt:
- CONNECT hat das Protokoll-Level
05und nach dem Keep Alive eine Properties-Länge. - CONNACK wächst um einen Reason Code und Properties, die oft Limits wie Maximum Packet Size, Topic Alias Maximum oder Maximum QoS ankündigen.
- PUBLISH hat nach dem Topic (und dem Packet Identifier) eine Properties-Länge, sodass ein QoS-0-Publish ohne Properties ein Byte größer ist.
- DISCONNECT kann einen Reason Code und Properties tragen; so erklärt der Broker, warum er eine Verbindung geschlossen hat.
Properties wie Topic Alias, Message Expiry Interval und User Properties ermöglichen die meisten Funktionen von MQTT 5.
Häufige Kodierungsfehler und wie Sie sie erkennen
Wenn Sie einen eigenen Client schreiben oder MQTT auf einem Gateway parsen, etwa auf einem Edge-Gerät neben einer SPS, prüfen Sie zuerst Folgendes:
- Remaining Length als festes Zwei-Byte-Feld geschrieben. Funktioniert bei kleinen Paketen, bricht oberhalb von 127 Byte.
- String-Längen in Zeichen statt in Bytes gezählt. Topic-Namen mit Nicht-ASCII-Zeichen haben mehr Bytes als Zeichen, was fehlerhafte Pakete erzeugt.
- Fehlender Packet Identifier bei QoS 1 oder 2. Der Broker liest einen Teil Ihres Payloads als Identifier, das Topic ist aber korrekt, sodass der Fehler wie beschädigte Daten aussieht.
- Falsche reservierte Flags bei SUBSCRIBE oder PUBREL, die Broker als fehlerhaft behandeln müssen, woraufhin sie die Verbindung schließen.
- Vergessene leere Properties-Länge beim Umstellen eines Clients auf MQTT 5.
Wireshark markiert fehlerhafte Pakete deutlich, und ein MQTT-5-Broker antwortet im DISCONNECT mit dem Reason Code 0x81 Malformed Packet, was sich viel schneller diagnostizieren lässt als ein stilles Schließen. Die maßgeblichen Definitionen finden Sie in der OASIS-MQTT-Spezifikation.
Pakete gegen einen echten ConnectMQ-Broker untersuchen
Pakete zu lesen ist der schnellste Weg, MQTT zu verstehen, und das MQTT-Tutorial baut mit Sessions, QoS und Retained Messages auf diesem Fundament auf. ConnectMQ betreibt den öffentlichen MQTT-Broker unter broker.connectmq.com genau für diese Art von Experiment und private Broker auf TLS-Port 8883, sobald echte Daten im Spiel sind.
Vom Paketmitschnitt zur Produktion mit ConnectMQ
Wenn Ihr Client bereit ist, bietet Ihnen ein verwalteter MQTT-Broker verschlüsselte Endpunkte, ausgegebene Zugangsdaten und ein Portal mit browserbasiertem Client und Live-Topic-Explorer. Erstellen Sie unter app.connectmq.com einen kostenlosen Serverless-Broker, ganz ohne Kreditkarte, oder lesen Sie mehr zur Wahl eines Cloud-MQTT-Brokers.
Häufig gestellte Fragen
Wie groß ist das kleinste MQTT-Paket?
Zwei Byte. PINGREQ, PINGRESP und ein DISCONNECT in MQTT 3.1.1 bestehen nur aus dem Fixed Header: einem Byte für Typ und Flags und einem Byte Remaining Length mit dem Wert null.
Was umfasst die Remaining Length?
Sie zählt die Bytes von Variable Header und Payload, nicht aber den Fixed Header selbst. Sie wird als Variable Byte Integer von einem bis vier Byte kodiert.
Warum kann ich mein Passwort in Wireshark lesen?
Weil Port 1883 unverschlüsselt ist und CONNECT Benutzername und Passwort im Payload überträgt. Verwenden Sie für echte Zugangsdaten TLS auf Port 8883 oder sichere WebSockets auf 8084.
Enthält das PUBLISH-Paket die Payload-Länge?
Nein. Die Payload-Länge ergibt sich implizit: Sie ist die Remaining Length abzüglich der Größe des Variable Headers, der den Topic-Namen, bei QoS 1 und 2 den Packet Identifier und in MQTT 5 die Properties enthält.



