A estrutura de pacotes MQTT é propositalmente compacta: todo pacote começa com um fixed header de apenas dois bytes, seguido de um variable header opcional e de um payload opcional. Este passo a passo decodifica pacotes CONNECT, CONNACK e PUBLISH reais byte a byte, para que você consiga ler uma captura do Wireshark ou um hex dump com segurança e identificar bugs de codificação nos seus próprios clientes.
As Três Partes de Todo Pacote MQTT
Todo pacote de controle MQTT segue o mesmo layout:
| Parte | Presente em | Conteúdo |
|---|---|---|
| Fixed header | Todos os pacotes | Tipo do pacote, flags, remaining length |
| Variable header | A maioria dos pacotes | Campos específicos do pacote, como nome do topic, packet identifier, nome do protocolo e, no MQTT 5, properties |
| Payload | Alguns pacotes | Client ID e credenciais no CONNECT, mensagem da aplicação no PUBLISH, filtros de topic no SUBSCRIBE |
Os menores pacotes, PINGREQ, PINGRESP e um DISCONNECT do MQTT 3.1.1, são apenas o fixed header de dois bytes. Essa economia é uma grande parte do motivo pelo qual o MQTT funciona bem em links lentos, e é por isso que entender a estrutura ajuda quando você estima o consumo de banda MQTT.
O Fixed Header
O primeiro byte se divide em duas metades:
Bit: 7 6 5 4 | 3 2 1 0
packet type | flags
Os quatro bits altos guardam o tipo do pacote, de 1 (CONNECT) a 15 (AUTH). Os quatro bits baixos são flags. Na maioria dos pacotes as flags são valores fixos e reservados; só o PUBLISH as usa de forma significativa:
| Bit | Significado no PUBLISH |
|---|---|
| 3 | DUP: é uma reentrega de uma mensagem QoS 1 ou 2 |
| 2-1 | Nível de QoS: 00, 01 ou 10 |
| 0 | RETAIN: o broker deve armazenar esta mensagem |
Assim, um primeiro byte 0x30 é um PUBLISH QoS 0, 0x32 é QoS 1, 0x31 é uma mensagem QoS 0 retida e 0x3A é uma reentrega QoS 1 com DUP ativado. SUBSCRIBE, UNSUBSCRIBE e PUBREL precisam ter as flags 0010, e é por isso que aparecem como 0x82, 0xA2 e 0x62. A referência de pacotes de controle MQTT lista cada tipo com seu primeiro byte.
Codificação do Remaining Length
Depois do primeiro byte vem o remaining length: o número de bytes do variable header mais o payload, sem contar o próprio fixed header. Ele usa um variable byte integer, de um a quatro bytes. Cada byte carrega sete bits de valor, e o bit mais alto indica "vem outro byte em seguida".
| Faixa de valores | Bytes usados |
|---|---|
| 0 a 127 | 1 |
| 128 a 16.383 | 2 |
| 16.384 a 2.097.151 | 3 |
| 2.097.152 a 268.435.455 | 4 |
Exemplo: um remaining length de 321. Como 321 = 2 x 128 + 65, o primeiro byte é 65 com o bit de continuação ativado (0xC1) e o segundo byte é 2 (0x02). O máximo de quatro bytes é a origem do tão citado teto de 256 MB por pacote do protocolo; o guia sobre o limite de tamanho de mensagem MQTT explica por que os brokers reais definem limites bem menores.
Aqui está um decodificador que você pode usar ao ler hex dumps:
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 # valor, número de bytes usados
multiplier *= 128
raise ValueError("Malformed remaining length")
print(decode_remaining_length(bytes([0x30, 0xC1, 0x02]))) # (321, 2)
Analisando uma Sessão Real
Execute este comando para publicar uma mensagem no servidor MQTT público (gratuito) com MQTT 3.1.1, que o mosquitto_pub usa por padrão:
mosquitto_pub -h broker.connectmq.com -p 1883 -i demo-a1b2 \
-t "connectmq/demo/a1b2/t" -m "21.5" -d
A porta 1883 não é criptografada, então uma captura de pacotes mostra todos os bytes. No Wireshark, capture na sua interface de rede e aplique o filtro de exibição mqtt. O Wireshark decodifica a porta 1883 como MQTT automaticamente; você verá CONNECT, CONNACK, PUBLISH e DISCONNECT. Use o seu próprio segmento de topic aleatório e lembre-se de que tudo no broker público pode ser lido por outras pessoas.
CONNECT
O primeiro pacote do cliente tem 23 bytes:
10 15 Fixed header: type 1 (CONNECT), remaining length 21
00 04 4D 51 54 54 Protocol name: length 4, "MQTT"
04 Protocol level: 4 = MQTT 3.1.1
02 Connect flags: Clean Session
00 3C Keep alive: 60 seconds
00 09 64 65 6D 6F 2D 61 31 62 32 Payload: client ID length 9, "demo-a1b2"
O byte de connect flags merece uma análise mais atenta. O bit 1 é Clean Session, o bit 2 é Will Flag, os bits 3-4 são Will QoS, o bit 5 é Will Retain, o bit 6 é Password Flag e o bit 7 é User Name Flag. Um cliente que se conecta com usuário, senha e clean session enviaria 0xC2. Se essas flags estiverem ativadas, o usuário e a senha vêm depois do client ID no payload, cada um com um prefixo de tamanho de dois bytes. Esse é um dos motivos para sempre usar TLS na porta 8883 com credenciais reais: na porta 1883 elas trafegam em texto puro.
CONNACK
O broker responde com quatro bytes:
20 02 00 00
Tipo 2, remaining length 2, Session Present 0, código de retorno 0 (aceito). Qualquer outro byte final é uma recusa; o guia de códigos de retorno do CONNACK MQTT decodifica cada valor.
PUBLISH
A mensagem em si tem 29 bytes:
30 1B Fixed header: PUBLISH, QoS 0, no retain; remaining length 27
00 15 Topic length: 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"
Repare no que está faltando: não há packet identifier, porque mensagens QoS 0 nunca são confirmadas. Com -q 1, o primeiro byte passa a ser 0x32, dois bytes de packet identifier aparecem depois do topic e o broker responde com um PUBACK de quatro bytes (40 02 mais o identificador). Também não existe campo de tamanho do payload; o payload é simplesmente o que sobra depois do variable header, de acordo com o remaining length.
DISCONNECT
Por fim, o cliente envia E0 00: tipo 14, remaining length 0. No MQTT 3.1.1 esse é o pacote inteiro.
O Que Muda no MQTT 5
O MQTT 5 mantém o mesmo esqueleto, mas adiciona properties ao variable header da maioria dos pacotes. Uma seção de properties começa com o seu próprio tamanho em variable byte integer, seguido de pares identificador-valor. Mesmo sem properties, o byte de tamanho 00 está presente.
Para a mesma sessão sobre MQTT 5:
- O CONNECT tem protocol level
05e um tamanho de properties depois do keep alive. - O CONNACK cresce para incluir um reason code e properties, muitas vezes anunciando limites como Maximum Packet Size, Topic Alias Maximum ou Maximum QoS.
- O PUBLISH tem um tamanho de properties depois do topic (e do packet identifier), então um publish QoS 0 sem properties fica um byte maior.
- O DISCONNECT pode carregar um reason code e properties, que é como o broker explica por que fechou uma conexão.
Properties como Topic Alias, Message Expiry Interval e User Properties são o que viabilizam a maioria dos recursos do MQTT 5.
Bugs de Codificação Comuns e Como Identificá-los
Se você escreve um cliente próprio ou faz parsing de MQTT em um gateway, verifique estes pontos primeiro:
- Remaining length escrito como um campo fixo de dois bytes. Funciona para pacotes pequenos e quebra acima de 127 bytes.
- Tamanho de strings contado em caracteres em vez de bytes. Nomes de topic com caracteres não ASCII têm mais bytes do que caracteres, o que gera pacotes malformados.
- Packet identifier ausente em QoS 1 ou 2. O broker lê parte do seu payload como identificador e o topic fica correto, então o bug parece dado corrompido.
- Flags reservadas erradas em SUBSCRIBE ou PUBREL, que os brokers precisam tratar como malformadas, fechando a conexão.
- Esquecer o tamanho de properties vazio ao migrar um cliente para o MQTT 5.
O Wireshark marca pacotes malformados com clareza, e um broker MQTT 5 responde com o reason code 0x81 Malformed Packet no DISCONNECT, o que é muito mais rápido de diagnosticar do que um fechamento silencioso. Para as definições oficiais, consulte a especificação MQTT da OASIS.
Inspecione Pacotes em um Broker ConnectMQ Real
Ler pacotes é a forma mais rápida de entender o MQTT, e o tutorial de MQTT parte dessa base para abordar sessões, QoS e retained messages. A ConnectMQ mantém o servidor MQTT público em broker.connectmq.com justamente para esse tipo de experimento, que não deve envolver dados de produção, e brokers privados na porta TLS 8883 quando dados reais entram em cena.
Das Capturas de Pacotes para a Produção com a ConnectMQ
Quando o seu cliente estiver pronto, um broker MQTT gerenciado oferece endpoints criptografados, credenciais emitidas e um portal com cliente no navegador e explorador de topics ao vivo. Crie um broker Serverless gratuito em app.connectmq.com, sem cartão de crédito, ou saiba mais sobre como escolher um broker MQTT na nuvem.
Perguntas frequentes
Qual é o tamanho do menor pacote MQTT?
Dois bytes. PINGREQ, PINGRESP e um DISCONNECT do MQTT 3.1.1 consistem apenas no fixed header: um byte para tipo e flags e um byte de remaining length igual a zero.
O que o remaining length inclui?
Ele conta os bytes do variable header e do payload, mas não o próprio fixed header. É codificado como um variable byte integer de um a quatro bytes.
Por que consigo ler minha senha no Wireshark?
Porque a porta 1883 não é criptografada, e o CONNECT carrega o usuário e a senha no payload. Use TLS na porta 8883 ou WebSockets seguros na 8084 para qualquer credencial real.
O pacote PUBLISH inclui o tamanho do payload?
Não. O tamanho do payload é implícito: é o remaining length menos o tamanho do variable header, que inclui o nome do topic, o packet identifier em QoS 1 e 2 e as properties no MQTT 5.



