Dokumentation und Vergleichstabellen sagen dir, was ein Broker kann. Entscheidend ist, was dein Broker mit deiner Konfiguration und deinen Zugangsdaten tatsächlich erlaubt. Am schnellsten und zuverlässigsten findest du das heraus, indem du ihn sondierst: mit MQTT 5 verbinden, die CONNACK-Properties lesen, dann jedes Feature ausprobieren und auf die zurückgegebenen Reason Codes achten.
Dieser Artikel erklärt, welche Signale du prüfen solltest, und liefert ein Python-Script (paho-mqtt 2.x), das für jeden Broker einen Fähigkeitsbericht ausgibt.
Was ein Broker im CONNACK verrät
Ein MQTT-5-Broker teilt seine Limits im CONNACK-Paket mit. Die wichtige Regel: Eine Property wird nur gesendet, wenn sie vom Standard abweicht. Ein fehlendes RetainAvailable heißt nicht „unbekannt“, sondern „ja“.
| CONNACK-Property | Standard, wenn sie fehlt | Was bei Einschränkung bricht |
|---|---|---|
| Maximum QoS | 2 | Publishes darüber bekommen 0x9B QoS not supported, die Verbindung wird geschlossen |
| Retain Available | ja | Retained Publishes bekommen 0x9A Retain not supported |
| Wildcard Subscription Available | ja | Abos mit + / # bekommen 0xA2 Wildcard Subscriptions not supported |
| Shared Subscription Available | ja | $share/...-Abos bekommen 0x9E Shared Subscriptions not supported |
| Subscription Identifiers Available | ja | Abos mit Identifier bekommen 0xA1 |
| Receive Maximum | 65.535 | Wie viele QoS-1/2-Nachrichten du gleichzeitig unbestätigt an den Broker senden darfst |
| Maximum Packet Size | kein Limit (Protokollmaximum) | Größere Pakete bekommen 0x95 Packet too large |
| Topic Alias Maximum | 0 (keine Aliase erlaubt) | Ein nicht erlaubter Alias führt zu 0x94 Topic Alias invalid |
| Server Keep Alive | dein Wert | Der Broker überschreibt deinen Keep-Alive; dein Client muss seinen Wert verwenden |
| Assigned Client Identifier | — | Wird gesendet, wenn du dich mit leerer Client ID verbindest |
MQTT 3.1.1 kennt nichts davon. Ein 3.1.1-Broker sagt dir nur, ob die Verbindung angenommen wurde — alles andere musst du durch Ausprobieren testen. Jeden dieser Codes kannst du in der Reason-Code-Referenz nachschlagen.
Das Sonden-Script
Das folgende Script führt sechs Prüfungen aus: Protokollversionen, CONNACK-Properties, gewährte QoS pro Abo, Wildcard- und Shared Subscriptions, Retained Messages und eine QoS-2-Zustellung Ende zu Ende. Konfiguriere es über Umgebungsvariablen und lass es gegen jede relevante Umgebung laufen.
"""Report which MQTT features a broker supports. Requires paho-mqtt >= 2.0."""
import os, threading, time, uuid
import paho.mqtt.client as mqtt
HOST = os.environ.get("MQTT_HOST", "broker.connectmq.com")
PORT = int(os.environ.get("MQTT_PORT", "1883"))
USER, PASS = os.environ.get("MQTT_USER"), os.environ.get("MQTT_PASS")
BASE = f"testmqtt/probe/{uuid.uuid4().hex[:8]}"
DEFAULTS = { # value that applies when the broker omits the property
"MaximumQoS": 2, "RetainAvailable": 1, "WildcardSubscriptionAvailable": 1,
"SharedSubscriptionAvailable": 1, "SubscriptionIdentifierAvailable": 1,
"ReceiveMaximum": 65535, "MaximumPacketSize": "no limit",
"TopicAliasMaximum": 0, "ServerKeepAlive": "client value",
}
def connect(protocol=mqtt.MQTTv5, timeout=10):
c = mqtt.Client(mqtt.CallbackAPIVersion.VERSION2,
client_id=f"probe-{uuid.uuid4().hex[:8]}", protocol=protocol)
if USER:
c.username_pw_set(USER, PASS)
if PORT == 8883:
c.tls_set()
done, res = threading.Event(), {}
def on_connect(client, userdata, flags, reason_code, properties):
res.update(rc=reason_code, props=properties)
done.set()
c.on_connect = on_connect
c.connect(HOST, PORT, keepalive=30)
c.loop_start()
if not done.wait(timeout):
c.loop_stop()
raise TimeoutError("no CONNACK")
return c, res["rc"], res["props"]
def close(*clients):
for c in clients:
c.disconnect()
c.loop_stop()
def subscribe(c, topic_filter, qos=2, timeout=5):
done, res = threading.Event(), {}
def on_subscribe(client, userdata, mid, reason_code_list, properties):
res["rc"] = reason_code_list[0]
done.set()
c.on_subscribe = on_subscribe
c.subscribe(topic_filter, qos=qos)
return res["rc"] if done.wait(timeout) else "no SUBACK"
def show(label, value):
print(f" {label:34} {value}")
def main():
print(f"Broker {HOST}:{PORT} (test topics under {BASE})\n")
print("Protocol versions")
for name, proto in (("MQTT 3.1.1", mqtt.MQTTv311), ("MQTT 5", mqtt.MQTTv5)):
try:
c, rc, _ = connect(proto)
show(name, "OK" if not rc.is_failure else f"refused: {rc}")
close(c)
except Exception as e:
show(name, f"FAILED ({e})")
c, rc, props = connect()
if rc.is_failure:
raise SystemExit(f"MQTT 5 connect refused: {rc}")
print("\nCONNACK properties")
for name, default in DEFAULTS.items():
value = getattr(props, name, None)
show(name, value if value is not None else f"{default} (default)")
print("\nSubscriptions (granted QoS or reason code)")
for q in (0, 1, 2):
show(f"QoS {q} subscription", subscribe(c, f"{BASE}/qos/{q}", qos=q))
show("Wildcard filter (+)", subscribe(c, f"{BASE}/+/wild", qos=1))
show("Shared subscription ($share)", subscribe(c, f"$share/probe/{BASE}/shared", qos=1))
print("\nActive tests")
got = {}
received = threading.Event()
def on_message(client, userdata, msg):
got[msg.topic] = msg
received.set()
c.on_message = on_message
# End-to-end QoS 2: publish at 2 to a QoS 2 subscription, check delivered QoS
received.clear()
c.publish(f"{BASE}/qos/2", b"qos2-probe", qos=2)
if received.wait(5):
show("QoS 2 delivery", f"delivered at QoS {got[f'{BASE}/qos/2'].qos}")
else:
show("QoS 2 delivery", "NOT delivered")
# Retained message: publish, then subscribe with a fresh client
topic = f"{BASE}/retain"
try:
c.publish(topic, b"retained-probe", qos=1, retain=True).wait_for_publish(5)
sub, _, _ = connect()
hit = threading.Event()
sub.on_message = lambda client, u, m: m.retain and hit.set()
subscribe(sub, topic, qos=1)
show("Retained message", "OK" if hit.wait(5) else "NOT delivered")
close(sub)
c.publish(topic, b"", qos=1, retain=True).wait_for_publish(5) # clear it
except Exception as e:
show("Retained message", f"FAILED ({e})")
close(c)
if __name__ == "__main__":
main()Starte es mit deinen Verbindungsdaten:
pip install "paho-mqtt>=2.0"
MQTT_HOST=broker.connectmq.com MQTT_PORT=8883 \
MQTT_USER="$MQTT_USER" MQTT_PASS="$MQTT_PASS" python probe.pyGewährte QoS-Werte erscheinen als paho-ReasonCode-Objekte: Granted QoS 2 bedeutet, dass der Broker das Abo auf dieser Stufe akzeptiert hat; ein Code ab 0x80 (zum Beispiel Not authorized oder Shared Subscriptions not supported) ist eine Ablehnung. Wird die Shared Subscription akzeptiert und willst du wissen, ob Nachrichten wirklich verteilt werden, nutze den Shared-Subscription-Test.
Den Bericht lesen
Typische Befunde und was zu tun ist:
- MQTT 5 abgelehnt, 3.1.1 OK — ältere Broker-Version oder ein Listener, der auf 3.1.1 beschränkt ist. Dein MQTT-5-Client braucht einen Fallback, oder der Broker ein Upgrade. Siehe MQTT 5 vs. 3.1.1.
- MaximumQoS 1 — üblich bei Cloud-IoT-Diensten und bei Brokern, die auf Durchsatz getrimmt sind. Ein QoS-2-Publish trennt deinen Client mit
0x9B; plane mit QoS 1 und idempotenten Handlern. - Abo mit niedrigerer QoS als angefragt gewährt — das ist zulässig. Der Broker stuft herab, statt abzulehnen, und Nachrichten kommen mit der gewährten Stufe an. Prüfe in deinem Client die gewährte QoS, nicht nur die angefragte.
- RetainAvailable 0 oder Retained Message nicht zugestellt — Geräte, die sich auf retained State verlassen (letzter Wert beim Abonnieren), wirken kaputt. Details unter Retain nicht unterstützt.
- TopicAliasMaximum 0 — der Broker akzeptiert keine Topic Aliases von Clients; Bibliotheken, die Aliase automatisch vergeben, müssen das abschalten.
- MaximumPacketSize gesetzt — vergleiche es mit deinem größten Payload plus Topic und Properties, etwa mit dem Paketgrößen-Rechner.
- Not authorized bei einem Abo — kein fehlendes Feature, sondern eine ACL. Lass die Sonde mit dem Benutzer deiner Geräte laufen und lies MQTT-ACL-Berechtigungen testen.
Dieselbe Prüfung in JavaScript
Mit MQTT.js 5 stecken die CONNACK-Properties im Paket, das an das connect-Event übergeben wird:
import mqtt from 'mqtt';
const client = mqtt.connect('mqtts://broker.connectmq.com:8883', {
protocolVersion: 5,
username: process.env.MQTT_USER,
password: process.env.MQTT_PASS,
});
client.on('connect', (connack) => {
// Absent keys mean the spec default applies
console.log(connack.properties ?? '(no properties: all defaults)');
client.subscribe('$share/probe/testmqtt/probe/js', { qos: 2 }, (err, granted) => {
console.log(err ?? granted); // granted[0].qos is the granted QoS or a reason code
client.end();
});
});
client.on('error', (err) => console.error('connect failed:', err.message));Die Property-Namen sind hier in camelCase: maximumQoS, retainAvailable, maximumPacketSize, topicAliasMaximum, wildcardSubscriptionAvailable, sharedSubscriptionAvailable, subscriptionIdentifiersAvailable, receiveMaximum und serverKeepAlive.
Kein Code? mqtt test nutzen
Die HiveMQ MQTT CLI hat diese Sonde eingebaut: mqtt test -h broker.connectmq.com gibt die MQTT-3- und MQTT-5-Fähigkeiten aus, einschließlich maximaler Topic-Länge und Payload-Größe, die durch Ausprobieren gemessen werden. Die mqtt-cli-Anleitung erklärt die Ausgabe. Für eine schnelle manuelle Prüfung verbindest du dich mit dem Online-MQTT-Client per MQTT 5 und probierst einen Retained Publish und ein $share/-Abo.
Teil der Pipeline machen
Fähigkeiten ändern sich unbemerkt: ein Broker-Upgrade, ein neuer Listener, ein anderer Tarif bei einem Managed Service. Speichere die Ausgabe der Sonde im Repository und vergleiche sie in der CI:
python probe.py | sed -E 's#testmqtt/probe/[0-9a-f]+#testmqtt/probe/X#' > capabilities.txt
git diff --exit-code capabilities.txtDas sed entfernt das zufällige Topic-Suffix, damit im Diff nur echte Änderungen auftauchen. Kombiniere das mit den Prüfungen aus MQTT-Broker testen, und du erfährst von einem fehlenden Feature, bevor es ein Gerät im Feld tut.
Sonden-Topics im Browser beobachten
Eine fünfminütige Sonde ist besser als ein Nachmittag mit dem Debuggen „zufälliger“ Verbindungsabbrüche. Lies das CONNACK, teste jedes Feature, auf das du dich verlässt, und halte den Bericht unter Versionskontrolle, damit Unterschiede zwischen Brokern und Umgebungen nie überraschen.
Häufig gestellte Fragen
Wie erkenne ich, ob ein Broker MQTT 5 unterstützt?
Verbinde dich mit Protokollversion 5. Ein Broker mit MQTT-5-Unterstützung antwortet mit einem MQTT-5-CONNACK, meist inklusive Properties. Andernfalls kommt 'unacceptable protocol version' (0x01 bei 3.1.1, 0x84 bei MQTT 5) zurück oder die Verbindung wird geschlossen.
Fehlt eine CONNACK-Property, heißt das, das Feature wird nicht unterstützt?
Nein, im Gegenteil. Bei MQTT 5 bedeutet eine fehlende Property, dass der Standardwert gilt: Retain, Wildcards, Shared Subscriptions und Subscription Identifiers sind verfügbar, Maximum QoS ist 2 und Receive Maximum 65.535. Broker senden nur Properties, die diese Standards einschränken oder ändern.
Kann ein Broker ein Feature unterstützen, es aber meinem Client verweigern?
Ja. ACLs, Einstellungen pro Listener und Tarife bei Managed Services können QoS, Retain, Wildcards oder Topics für bestimmte Benutzer einschränken. Deshalb sollte die Sonde mit denselben Zugangsdaten und demselben Port laufen wie deine Geräte.


