Logs zeigen, was ein Client zu senden glaubt. Ein Paketmitschnitt zeigt, was tatsächlich über die Leitung ging. Wireshark bringt einen vollständigen MQTT-Dissector mit: Er dekodiert CONNECT-Flags, Topic-Namen, QoS, Packet Identifier, MQTT-5-Properties und Reason Codes. So siehst du in Sekunden, ob eine fehlende Nachricht nie publiziert, nie bestätigt oder nie zugestellt wurde.
Diese Anleitung zeigt, wie du MQTT-Traffic mitschneidest, welche Anzeigefilter du täglich brauchst, wie du TLS auf Port 8883 entschlüsselst, und ein paar tshark-Einzeiler für Server ohne GUI.
Den richtigen Traffic mitschneiden
Starte Wireshark auf der Maschine, auf der der Client läuft (oder der Broker, falls du ihn betreibst), und wähle das Interface, über das der Traffic läuft: deinen WLAN- oder Ethernet-Adapter für entfernte Broker, das Loopback-Interface für einen Broker auf localhost.
Nutze einen Capture-Filter, damit die Datei klein bleibt. Capture-Filter verwenden BPF-Syntax und werden vor dem Start gesetzt:
tcp port 1883 # unverschlüsseltes MQTT
tcp port 1883 or tcp port 8883 # unverschlüsselt und TLS
host broker.connectmq.com # alles von und zum BrokerErzeuge dann den Traffic, den du analysieren willst — zum Beispiel einen Subscriber und einen Publisher auf einem eindeutigen Topic:
mosquitto_sub -h broker.connectmq.com -p 1883 -t 'testmqtt/wireshark/#' -q 1 -v &
mosquitto_pub -h broker.connectmq.com -p 1883 -t 'testmqtt/wireshark/hello' -q 1 -m 'ping'Auf einem Server ist tcpdump -w oft bequemer; die Datei öffnest du anschließend in Wireshark. Siehe MQTT mit tcpdump mitschneiden.
Die Anzeigefilter, die du brauchst
Anzeigefilter werden nach dem Mitschnitt angewendet und nutzen Wiresharks Feldnamen. Die wichtigsten Felder des MQTT-Dissectors:
| Ziel | Anzeigefilter |
|---|---|
| Alle MQTT-Pakete | mqtt |
| Nur CONNECT / CONNACK | mqtt.msgtype == 1 || mqtt.msgtype == 2 |
| Nur PUBLISH | mqtt.msgtype == 3 |
| Ein Topic oder Topic-Präfix | mqtt.topic contains "testmqtt/wireshark" |
| Der CONNECT eines Clients | mqtt.clientid == "sensor-01" |
| Abgelehnte Verbindungen | mqtt.conack.val != 0 |
| Retained Publishes | mqtt.retain == 1 |
| PUBLISH mit QoS 1 und 2 | mqtt.msgtype == 3 && mqtt.qos > 0 |
| Keep-Alive-Traffic | mqtt.msgtype == 12 || mqtt.msgtype == 13 |
| Zurückgesetzte Verbindungen | tcp.flags.reset == 1 |
| TCP-Retransmissions | tcp.analysis.retransmission |
Die numerischen Werte von mqtt.msgtype sind die MQTT-Control-Packet-Typen:
| Wert | Paket | Wert | Paket |
|---|---|---|---|
| 1 | CONNECT | 8 | SUBSCRIBE |
| 2 | CONNACK | 9 | SUBACK |
| 3 | PUBLISH | 10 | UNSUBSCRIBE |
| 4 | PUBACK | 11 | UNSUBACK |
| 5 | PUBREC | 12 | PINGREQ |
| 6 | PUBREL | 13 | PINGRESP |
| 7 | PUBCOMP | 14 | DISCONNECT |
Typ 15 ist AUTH (nur MQTT 5). Den Aufbau auf Byte-Ebene hinter diesen Feldern erklärt MQTT-Paketstruktur.
CONNECT und CONNACK lesen
Wähle ein CONNECT-Paket und klappe MQ Telemetry Transport Protocol auf. Du siehst:
- Protokollname und -version —
MQTTmit Version 4 (3.1.1) oder 5. Akzeptiert der Broker die Version nicht, antwortet er mit einem unacceptable protocol version-Fehler. - Connect Flags — Clean Session / Clean Start, Will Flag, Will QoS, Will Retain und ob Username und Passwort enthalten sind.
- Keep Alive — das Intervall in Sekunden, das der Client zugesagt hat.
- Client ID und auf dem unverschlüsselten Port 1883 Username und Passwort im Klartext.
- MQTT-5-Properties wie Session Expiry Interval, Receive Maximum und Maximum Packet Size.
Das folgende CONNACK enthält das Ergebnis. Bei MQTT 3.1.1 ist es ein Return Code (0 heißt akzeptiert), bei MQTT 5 ein Reason Code wie 0x86 Bad User Name or Password oder 0x87 Not authorized, manchmal mit Reason String. Jeden Wert kannst du im Reason-Code-Tool nachschlagen. Die feldgenaue Aufschlüsselung findest du unter das MQTT-CONNECT-Paket.
Kommt gar kein CONNACK und der Broker schickt stattdessen ein TCP-RST oder FIN, hat er die Verbindung abgelehnt, bevor MQTT überhaupt ins Spiel kam — oft ein TLS-Listener, der unverschlüsseltes MQTT empfängt, ein fehlerhaftes Paket oder ein Verbindungslimit.
Eine Nachricht Ende-zu-Ende verfolgen
Um zu beweisen, wo eine Nachricht verloren ging, verfolge sie durch den Mitschnitt:
- Filtere
mqtt.topic contains "testmqtt/wireshark"und finde den PUBLISH des Publishers. Notiere bei QoS 1 oder 2 seinen Message Identifier. - Filtere
mqtt.msgid == <id>, um den PUBACK (QoS 1) oder die Folge PUBREC / PUBREL / PUBCOMP (QoS 2) zu sehen. Kein PUBACK heißt: Der Broker hat die Nachricht nie angenommen — prüfe ACLs und Payload-Größe. - Hast du auch auf Subscriber-Seite mitgeschnitten, suche den PUBLISH, den der Broker ausgeliefert hat. Der Packet Identifier ist ein anderer, denn jeder Hop hat seinen eigenen.
Statistics → Flow Graph mit „Displayed packets" zeichnet den Austausch als Sequenzdiagramm — der schnellste Weg, eine fehlende Bestätigung zu entdecken.
MQTT über TLS entschlüsseln (Port 8883)
Auf Port 8883 siehst du nur TLS-Records, bis Wireshark die Sitzungsschlüssel hat. Der Standardweg dorthin ist eine Key-Log-Datei, die viele TLS-Bibliotheken schreiben können.
Python (paho-mqtt 2.x) — setze keylog_filename am SSL-Kontext:
import ssl
import paho.mqtt.client as mqtt
ctx = ssl.create_default_context()
ctx.keylog_filename = "/tmp/mqtt-tls-keys.log" # Python 3.8+
client = mqtt.Client(mqtt.CallbackAPIVersion.VERSION2)
client.tls_set_context(ctx)
client.connect("broker.connectmq.com", 8883)
client.loop_start()
client.publish("testmqtt/wireshark/tls", "hello over TLS", qos=1).wait_for_publish()
client.loop_stop()
client.disconnect()Node.js (MQTT.js) — nutze das eingebaute Flag, ohne Codeänderung:
node --tls-keylog=/tmp/mqtt-tls-keys.log publish.jsBrowser und curl beachten die Umgebungsvariable SSLKEYLOGFILE. Öffne dann in Wireshark Edit → Preferences → Protocols → TLS und trage unter (Pre)-Master-Secret log filename den Pfad zum Key-Log ein. Die TLS-Records auf 8883 werden zu dekodierten MQTT-Paketen, bei TLS 1.2 wie bei 1.3.
Schlägt schon der TLS-Handshake fehl, brauchst du keine Entschlüsselung: Der Alert-Record (etwa Unknown CA oder Handshake Failure) ist offen sichtbar. Siehe TLS handshake failed.
Nicht-Standard-Ports und WebSockets
Wireshark nimmt MQTT nur auf den registrierten Ports an. Lauscht dein Broker etwa auf 11883, klicke mit rechts auf ein Paket, wähle Decode As… und ordne diesen TCP-Port MQTT zu.
Bei MQTT über WebSockets (8083 / 8084) zeigt Wireshark den HTTP-Upgrade-Request und die WebSocket-Frames; die MQTT-Pakete stecken in binären Frames. Bei Browser-Apps ist es meist schneller, diese Frames direkt im Browser zu lesen — siehe MQTT in Chrome DevTools debuggen.
tshark-Einzeiler
tshark ist der Kommandozeilen-Zwilling von Wireshark und nutzt dieselben Filter:
# Live: Zeit, Pakettyp, Topic und Payload ausgeben
tshark -i any -f "tcp port 1883" -Y mqtt \
-T fields -e frame.time_relative -e mqtt.msgtype -e mqtt.topic -e mqtt.msg
# Aus einer Datei: PUBLISH-Pakete pro Topic zählen
tshark -r mqtt.pcap -Y "mqtt.msgtype == 3" -T fields -e mqtt.topic | sort | uniq -c | sort -rn
# Abstände zwischen Keep-Alive-Pings (auf Werte nahe am Keep-Alive-Limit achten)
tshark -r mqtt.pcap -Y "mqtt.msgtype == 12" -T fields -e frame.time_delta_displayedDer letzte hilft bei Keep-Alive-Timeouts: Übersteigen die Abstände das 1,5-Fache des Keep-Alive-Werts, darf der Broker den Client trennen.
Worauf du je nach Symptom achtest
| Symptom | Was der Mitschnitt zeigt |
|---|---|
| Client „verbindet nicht" | CONNECT, gefolgt von CONNACK mit Code ungleich null, oder RST ohne CONNACK |
| Nachrichten bei QoS 1 verloren | PUBLISH ohne PUBACK, nach dem Reconnect ein Duplikat (DUP-Flag gesetzt) |
| Zufällige Verbindungsabbrüche | Lücken bei PINGREQ, TCP-Retransmissions, dann FIN oder RST von einer Seite |
| Große Nachrichten verschwinden | Großer PUBLISH, danach schließt der Broker die Verbindung — mit der Maximum Packet Size vergleichen und Größen im Paketgrößen-Rechner prüfen |
| Subscriber empfängt nichts | SUBACK mit Fehlercode 0x80 oder ein Topic-Filter, der nicht passt |
Um einen Fall schnell nachzustellen, starte den Mitschnitt, während du aus dem Online-MQTT-Client publizierst, und sieh den Paketen in Echtzeit zu.
Wireshark ersetzt Raten durch Belege. Schneide gezielt mit, filtere nach Pakettyp und Topic, entschlüssele TLS nur bei Bedarf und bewahre einen Mitschnitt einer funktionierenden Sitzung auf — der Vergleich eines defekten Mitschnitts mit einem guten ist oft die schnellste Diagnose überhaupt.
Häufig gestellte Fragen
Wie lautet der Wireshark-Anzeigefilter für MQTT?
Mit 'mqtt' siehst du alle dekodierten MQTT-Pakete. Eingrenzen kannst du mit Feldern wie mqtt.msgtype == 3 für PUBLISH, mqtt.topic contains "sensors" für ein Topic oder mqtt.clientid == "sensor-01" für den CONNECT eines Clients.
Warum zeigt Wireshark auf Port 8883 TLS statt MQTT?
Die MQTT-Pakete sind verschlüsselt. Wireshark kann sie nur dekodieren, wenn du die TLS-Sitzungsschlüssel lieferst: Lass den Client eine Key-Log-Datei schreiben (SSLKEYLOGFILE, keylog_filename in Python, --tls-keylog in Node) und trage diese Datei in den TLS-Einstellungen von Wireshark ein.
Warum erscheint mein MQTT-Traffic nur als rohe TCP-Daten?
Wireshark dekodiert MQTT automatisch nur auf dem Standardport 1883 (und 8883 nach der Entschlüsselung). Für jeden anderen Port: Rechtsklick auf ein Paket, Decode As wählen und den TCP-Port auf MQTT setzen.

