Documentação e tabelas comparativas dizem o que um broker consegue fazer. O que importa é o que o seu broker, com a sua configuração e as suas credenciais, realmente permite. O jeito mais rápido e confiável de descobrir é sondá-lo: conectar com MQTT 5, ler as propriedades do CONNACK e depois testar cada recurso, olhando os reason codes que voltam.
Este artigo explica quais sinais verificar e traz um script Python (paho-mqtt 2.x) que imprime um relatório de capacidades para qualquer broker.
O que um broker informa no CONNACK
Um broker MQTT 5 anuncia seus limites no pacote CONNACK. A regra importante: uma propriedade só é enviada quando difere do padrão. A ausência de RetainAvailable não significa "desconhecido" — significa "sim".
| Propriedade do CONNACK | Padrão se ausente | O que quebra se for restrita |
|---|---|---|
| Maximum QoS | 2 | Publicar acima dela gera 0x9B QoS not supported e a conexão é fechada |
| Retain Available | sim | Publicações retidas recebem 0x9A Retain not supported |
| Wildcard Subscription Available | sim | Assinaturas com + / # recebem 0xA2 Wildcard Subscriptions not supported |
| Shared Subscription Available | sim | Assinaturas $share/... recebem 0x9E Shared Subscriptions not supported |
| Subscription Identifiers Available | sim | Assinaturas com identificador recebem 0xA1 |
| Receive Maximum | 65.535 | Quantas mensagens QoS 1/2 você pode ter em trânsito para o broker |
| Maximum Packet Size | sem limite (máximo do protocolo) | Pacotes maiores recebem 0x95 Packet too large |
| Topic Alias Maximum | 0 (aliases não aceitos) | Usar um alias que o broker não permitiu é 0x94 Topic Alias invalid |
| Server Keep Alive | o seu valor | O broker sobrescreve seu keep-alive; seu cliente precisa usar o valor dele |
| Assigned Client Identifier | — | Enviado quando você conecta com um client ID vazio |
O MQTT 3.1.1 não tem nada disso. Um broker 3.1.1 só diz se a conexão foi aceita, então tudo precisa ser verificado testando. Você pode consultar qualquer um desses códigos na referência de reason codes.
O script de sondagem
O script abaixo faz seis verificações: versões do protocolo, propriedades do CONNACK, QoS concedida por assinatura, assinaturas com wildcard e compartilhadas, mensagens retidas e uma entrega QoS 2 de ponta a ponta. Configure-o com variáveis de ambiente e rode em cada ambiente que importa para você.
"""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()Rode com os seus dados de conexão:
pip install "paho-mqtt>=2.0"
MQTT_HOST=broker.connectmq.com MQTT_PORT=8883 \
MQTT_USER="$MQTT_USER" MQTT_PASS="$MQTT_PASS" python probe.pyOs valores de QoS concedida aparecem como objetos ReasonCode do paho: Granted QoS 2 significa que o broker aceitou a assinatura nesse nível, enquanto um código 0x80 ou maior (por exemplo Not authorized ou Shared Subscriptions not supported) é uma recusa. Se a assinatura compartilhada for aceita e você quiser saber se as mensagens são realmente balanceadas, use o teste de assinaturas compartilhadas.
Como ler o relatório
Achados típicos e o que fazer com eles:
- MQTT 5 recusado, 3.1.1 OK — versão antiga do broker ou um listener limitado ao 3.1.1. Seu cliente MQTT 5 precisa fazer fallback, ou o broker precisa ser atualizado. Veja MQTT 5 vs 3.1.1.
- MaximumQoS 1 — comum em serviços de IoT na nuvem e em brokers configurados para throughput. Uma publicação QoS 2 vai desconectar seu cliente com
0x9B; projete para QoS 1 com handlers idempotentes. - Assinatura concedida com QoS menor que a solicitada — isso é permitido. O broker rebaixa em vez de recusar, e as mensagens chegam no nível concedido. Verifique a QoS concedida no seu cliente, não só a solicitada.
- RetainAvailable 0 ou mensagem retida não entregue — dispositivos que dependem de estado retido (último valor ao assinar) vão parecer quebrados. Detalhes em retain não suportado.
- TopicAliasMaximum 0 — o broker não aceita topic aliases dos clientes; bibliotecas que atribuem aliases automaticamente precisam desligar isso.
- MaximumPacketSize definido — compare com o seu maior payload mais topic e propriedades usando a calculadora de tamanho de pacote.
- Not authorized em uma assinatura — não é recurso faltando, é ACL. Rode a sonda com o usuário dos seus dispositivos e veja testar permissões de ACL no MQTT.
A mesma verificação em JavaScript
Com o MQTT.js 5, as propriedades do CONNACK estão no pacote passado ao evento connect:
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));Aqui os nomes das propriedades estão em camelCase: maximumQoS, retainAvailable, maximumPacketSize, topicAliasMaximum, wildcardSubscriptionAvailable, sharedSubscriptionAvailable, subscriptionIdentifiersAvailable, receiveMaximum e serverKeepAlive.
Sem código? Use o mqtt test
A HiveMQ MQTT CLI já tem essa sonda embutida: mqtt test -h broker.connectmq.com imprime as capacidades do MQTT 3 e do MQTT 5, incluindo tamanho máximo de topic e de payload medidos na prática. O guia do mqtt-cli explica a saída. Para uma checagem manual rápida, conecte com o cliente MQTT online usando MQTT 5 e teste uma publicação retida e uma assinatura $share/.
Coloque no seu pipeline
Capacidades mudam sem aviso: uma atualização do broker, um listener novo, outro plano num serviço gerenciado. Salve a saída da sonda no repositório e compare no CI:
python probe.py | sed -E 's#testmqtt/probe/[0-9a-f]+#testmqtt/probe/X#' > capabilities.txt
git diff --exit-code capabilities.txtO sed remove o sufixo aleatório do topic para que só mudanças reais apareçam no diff. Junte isso às verificações de como testar um broker MQTT e você vai saber de um recurso faltando antes de um dispositivo em campo.
Veja os topics da sonda no navegador
Uma sonda de cinco minutos vale mais que uma tarde depurando desconexões "aleatórias". Leia o CONNACK, teste cada recurso de que você depende e mantenha o relatório no controle de versão, para que diferenças entre brokers e ambientes nunca sejam surpresa.
Perguntas frequentes
Como saber se um broker suporta MQTT 5?
Conecte com a versão de protocolo 5. Um broker que suporta responde com um CONNACK do MQTT 5, geralmente com propriedades. Um que não suporta retorna 'unacceptable protocol version' (0x01 no 3.1.1, 0x84 no MQTT 5) ou fecha a conexão.
Se uma propriedade não vem no CONNACK, o recurso não é suportado?
Não, é o contrário. No MQTT 5, uma propriedade ausente significa que vale o padrão: retain, wildcards, assinaturas compartilhadas e identificadores de assinatura estão disponíveis, a QoS máxima é 2 e o receive maximum é 65.535. Brokers só enviam as propriedades que restringem ou alteram esses padrões.
Um broker pode suportar um recurso mas negá-lo ao meu cliente?
Sim. ACLs, configurações por listener e planos de serviços gerenciados podem restringir QoS, retain, wildcards ou topics para usuários específicos. Por isso a sonda deve rodar com as mesmas credenciais e a mesma porta dos seus dispositivos.


