---
title: MQTT
url: https://doc.liz6.com/en/networking/10-real-time-communication/03-MQTT
locale: en
area: networking
tags:
- networking
- real-time-communication
date: 2026-06-30
modified: 2026-07-16
description: The most concise pub/sub protocol designed for low-bandwidth, high-latency, and unreliable IoT networks—QoS 0/1/2 provide three levels of delivery guarantees, retained messages enable state synchronization, and will messages handle offline notifications. MQTT 5.0 adds enterprise features such as session expiry and shared subscriptions.
---

# MQTT

> The most concise pub/sub protocol designed for low-bandwidth, high-latency, and unreliable IoT networks—QoS 0/1/2 provide three levels of delivery guarantees, retained messages enable state synchronization, and will messages handle offline notifications. MQTT 5.0 adds enterprise features such as session expiry and shared subscriptions.

## Overview

MQTT (Message Queuing Telemetry Transport, 1999/2013) is the most concise pub/sub messaging protocol, specifically designed for low-bandwidth, high-latency, and unreliable IoT networks. Publishers do not connect directly to subscribers—all messages pass through a broker (Mosquitto/EMQX), and topics support wildcard filtering. QoS 0/1/2 provide three levels of delivery guarantees, while retained messages and will messages enable state synchronization and offline notifications. MQTT 5.0 (2019) adds enterprise features such as session expiry and shared subscriptions.

## Model

MQTT is a minimalist pub/sub protocol designed for low-bandwidth, high-latency, and unreliable networks (IoT, M2M). All messages pass through a central broker (Mosquitto, EMQX):

<svg viewBox="0 0 720 300" xmlns="http://www.w3.org/2000/svg" font-family="-apple-system,'Source Han Sans CN','Microsoft YaHei',sans-serif" role="img" aria-label="MQTT pub/sub model: messages are filtered by topic and distributed to matching subscribers via the Broker">
  <defs><marker id="mqtt-pubsub-arrow" markerWidth="10" markerHeight="8" refX="8" refY="3" orient="auto"><path d="M0,0 L8,3 L0,6 Z" fill="#475569"/></marker></defs>
  <rect width="720" height="300" fill="#ffffff"/>
  <text x="360" y="28" text-anchor="middle" font-size="17" font-weight="700" fill="#1f2933">Pub/Sub Model: Messages Distributed by Broker via Topic Filtering</text>

  <rect x="40" y="70" width="150" height="50" rx="8" fill="#4f46e5"/>
  <text x="115" y="90" text-anchor="middle" font-size="12" font-weight="700" fill="#ffffff">Temperature Sensor</text>
  <text x="115" y="108" text-anchor="middle" font-size="10" fill="#e0e7ff">Publisher</text>

  <line x1="190" y1="95" x2="330" y2="95" stroke="#475569" stroke-width="1.7" marker-end="url(#mqtt-pubsub-arrow)"/>
  <text x="260" y="80" text-anchor="middle" font-size="10" fill="#334155">publish "home/bedroom/temp" 23.5</text>

  <rect x="330" y="70" width="150" height="50" rx="8" fill="#0d9488"/>
  <text x="405" y="90" text-anchor="middle" font-size="13" font-weight="700" fill="#ffffff">Broker</text>
  <text x="405" y="108" text-anchor="middle" font-size="10" fill="#ccfbf1">Mosquitto / EMQX</text>

  <text x="405" y="152" text-anchor="middle" font-size="10.5" fill="#334155">→ Push to all matching subscribers</text>

  <line x1="405" y1="120" x2="150" y2="188" stroke="#475569" stroke-width="1.5" marker-end="url(#mqtt-pubsub-arrow)"/>
  <line x1="405" y1="120" x2="360" y2="188" stroke="#475569" stroke-width="1.5" marker-end="url(#mqtt-pubsub-arrow)"/>
  <line x1="405" y1="120" x2="570" y2="188" stroke="#475569" stroke-width="1.5" marker-end="url(#mqtt-pubsub-arrow)"/>

  <rect x="60" y="190" width="180" height="48" rx="8" fill="#f0fdfa" stroke="#99f6e4"/>
  <text x="150" y="210" text-anchor="middle" font-size="11" font-weight="700" fill="#115e59">"home/bedroom/temp"</text>
  <text x="150" y="228" text-anchor="middle" font-size="9.5" fill="#0f766e">Exact Match</text>

  <rect x="270" y="190" width="180" height="48" rx="8" fill="#f0fdfa" stroke="#99f6e4"/>
  <text x="360" y="210" text-anchor="middle" font-size="11" font-weight="700" fill="#115e59">"home/+/temp"</text>
  <text x="360" y="228" text-anchor="middle" font-size="9.5" fill="#0f766e">Single-level wildcard +</text>

  <rect x="480" y="190" width="180" height="48" rx="8" fill="#f0fdfa" stroke="#99f6e4"/>
  <text x="570" y="210" text-anchor="middle" font-size="11" font-weight="700" fill="#115e59">"home/#"</text>
  <text x="570" y="228" text-anchor="middle" font-size="9.5" fill="#0f766e">Multi-level wildcard # (including all sub-levels)</text>

  <rect x="40" y="252" width="640" height="40" rx="8" fill="#eef2ff" stroke="#c7d2fe"/>
  <text x="56" y="277" font-size="12" fill="#3730a3">The Broker only performs topic string matching and does not care about the message content; the + wildcard matches a single level, and # matches multiple levels and all sub-levels below.</text>
</svg>

## QoS

<svg viewBox="0 0 720 380" xmlns="http://www.w3.org/2000/svg" font-family="-apple-system,'Source Han Sans CN','Microsoft YaHei',sans-serif" role="img" aria-label="MQTT QoS 0/1/2 three-level delivery guarantees: reliability achieved through handshake counts">
  <defs><marker id="mqtt-qos-arrow" markerWidth="10" markerHeight="8" refX="8" refY="3" orient="auto"><path d="M0,0 L8,3 L0,6 Z" fill="#475569"/></marker></defs>
  <rect width="720" height="380" fill="#ffffff"/>
  <text x="360" y="28" text-anchor="middle" font-size="17" font-weight="700" fill="#1f2933">QoS 0/1/2: Delivery Guarantees Achieved Through Handshakes</text>

  <rect x="30" y="64" width="95" height="40" rx="6" fill="#e2e8f0" stroke="#cbd5e1"/>
  <text x="77" y="81" text-anchor="middle" font-size="12" font-weight="700" fill="#334155">QoS 0</text>
  <text x="77" y="98" text-anchor="middle" font-size="9.5" fill="#64748b">At most once</text>

  <rect x="135" y="64" width="110" height="40" rx="5" fill="#f1f5f9" stroke="#cbd5e1"/>
  <text x="190" y="81" text-anchor="middle" font-size="10.5" font-weight="700" fill="#334155">PUBLISH qos=0</text>
  <text x="190" y="98" text-anchor="middle" font-size="9" fill="#64748b">Pub→Broker, no acknowledgment</text>

  <line x1="245" y1="84" x2="263" y2="84" stroke="#475569" stroke-width="1.6" marker-end="url(#mqtt-qos-arrow)"/>

  <rect x="263" y="64" width="110" height="40" rx="5" fill="#f1f5f9" stroke="#cbd5e1"/>
  <text x="318" y="81" text-anchor="middle" font-size="10.5" font-weight="700" fill="#334155">PUBLISH qos=0</text>
  <text x="318" y="98" text-anchor="middle" font-size="9" fill="#64748b">Broker→Sub, no acknowledgment</text>

  <line x1="373" y1="84" x2="391" y2="84" stroke="#475569" stroke-width="1.6" marker-end="url(#mqtt-qos-arrow)"/>

  <rect x="391" y="64" width="140" height="40" rx="6" fill="#e2e8f0" stroke="#cbd5e1"/>
  <text x="461" y="81" text-anchor="middle" font-size="11" font-weight="700" fill="#334155">fire and forget</text>
  <text x="461" y="98" text-anchor="middle" font-size="9" fill="#64748b">No acknowledgment · No retry</text>

  <rect x="30" y="140" width="95" height="40" rx="6" fill="#ffedd5" stroke="#f97316"/>
  <text x="77" y="157" text-anchor="middle" font-size="12" font-weight="700" fill="#9a3412">QoS 1</text>
  <text x="77" y="174" text-anchor="middle" font-size="9.5" fill="#c2410c">At least once</text>

  <rect x="135" y="140" width="150" height="40" rx="5" fill="#ffedd5" stroke="#f97316"/>
  <text x="210" y="157" text-anchor="middle" font-size="10.5" font-weight="700" fill="#9a3412">PUBLISH qos=1,id=N</text>
  <text x="210" y="174" text-anchor="middle" font-size="9" fill="#c2410c">Publisher→Broker</text>

  <line x1="285" y1="160" x2="303" y2="160" stroke="#475569" stroke-width="1.6" marker-end="url(#mqtt-qos-arrow)"/>

  <rect x="303" y="140" width="150" height="40" rx="5" fill="#ffedd5" stroke="#f97316"/>
  <text x="378" y="157" text-anchor="middle" font-size="10.5" font-weight="700" fill="#9a3412">PUBACK id=N</text>
  <text x="378" y="174" text-anchor="middle" font-size="9" fill="#c2410c">Broker→Publisher · Acknowledged</text>

  <line x1="453" y1="160" x2="471" y2="160" stroke="#475569" stroke-width="1.6" marker-end="url(#mqtt-qos-arrow)"/>

  <rect x="471" y="140" width="130" height="40" rx="6" fill="#ffedd5" stroke="#f97316"/>
  <text x="536" y="157" text-anchor="middle" font-size="11" font-weight="700" fill="#9a3412">Possible duplicates</text>
  <text x="536" y="174" text-anchor="middle" font-size="9" fill="#c2410c">retransmission</text>

  <rect x="30" y="216" width="95" height="40" rx="6" fill="#dcfce7" stroke="#4ade80"/>
  <text x="77" y="233" text-anchor="middle" font-size="12" font-weight="700" fill="#166534">QoS 2</text>
  <text x="77" y="250" text-anchor="middle" font-size="9.5" fill="#15803d">Exactly once</text>

  <rect x="135" y="216" width="90" height="40" rx="5" fill="#dcfce7" stroke="#4ade80"/>
  <text x="180" y="233" text-anchor="middle" font-size="9.5" font-weight="700" fill="#166534">①PUBLISH</text>
  <text x="180" y="250" text-anchor="middle" font-size="8.5" fill="#15803d">Pub→Broker</text>

  <line x1="225" y1="236" x2="239" y2="236" stroke="#475569" stroke-width="1.5" marker-end="url(#mqtt-qos-arrow)"/>

  <rect x="239" y="216" width="90" height="40" rx="5" fill="#dcfce7" stroke="#4ade80"/>
  <text x="284" y="233" text-anchor="middle" font-size="9.5" font-weight="700" fill="#166534">②PUBREC</text>
  <text x="284" y="250" text-anchor="middle" font-size="8.5" fill="#15803d">Broker→Pub</text>

  <line x1="329" y1="236" x2="343" y2="236" stroke="#475569" stroke-width="1.5" marker-end="url(#mqtt-qos-arrow)"/>

  <rect x="343" y="216" width="90" height="40" rx="5" fill="#dcfce7" stroke="#4ade80"/>
  <text x="388" y="233" text-anchor="middle" font-size="9.5" font-weight="700" fill="#166534">③PUBREL</text>
  <text x="388" y="250" text-anchor="middle" font-size="8.5" fill="#15803d">Pub→Broker</text>

  <line x1="433" y1="236" x2="447" y2="236" stroke="#475569" stroke-width="1.5" marker-end="url(#mqtt-qos-arrow)"/>

  <rect x="447" y="216" width="90" height="40" rx="5" fill="#dcfce7" stroke="#4ade80"/>
  <text x="492" y="233" text-anchor="middle" font-size="9.5" font-weight="700" fill="#166534">④PUBCOMP</text>
  <text x="492" y="250" text-anchor="middle" font-size="8.5" fill="#15803d">Broker→Pub</text>

  <line x1="537" y1="236" x2="551" y2="236" stroke="#475569" stroke-width="1.5" marker-end="url(#mqtt-qos-arrow)"/>

  <rect x="551" y="216" width="130" height="40" rx="6" fill="#dcfce7" stroke="#4ade80"/>
  <text x="616" y="233" text-anchor="middle" font-size="10.5" font-weight="700" fill="#166534">No duplicates, no loss</text>
  <text x="616" y="250" text-anchor="middle" font-size="8.5" fill="#15803d">Four-way handshake confirmation</text>

  <rect x="30" y="290" width="660" height="68" rx="8" fill="#eef2ff" stroke="#c7d2fe"/>
  <text x="46" y="315" font-size="12.5" fill="#3730a3">The higher the QoS number, the more handshakes, the more reliable the delivery, but also the higher the latency and bandwidth overhead—</text>
  <text x="46" y="337" font-size="12.5" fill="#3730a3">MQTT leaves the choice to each message, rather than applying a one-size-fits-all global approach.</text>
</svg>

## MQTT CONNECT/CONNACK

```
CONNECT:
  protocol_name: "MQTT" (v3.1.1) or "MQIsdp" (v3.1)
  protocol_level: 4 (v3.1.1) or 5 (v5.0)
  flags: clean_session, will_flag, will_qos, will_retain, password_flag, username_flag
  keep_alive: seconds (publisher must send PINGREQ within keep_alive seconds)
  client_id: unique ID (or empty → broker assigns, v5.0 only)
  [will_topic, will_message] ← if will_flag=1
  [username, password]

CONNACK:
  session_present: 0 (new session), 1 (existing session resumed)
  return_code: 0=success, 1-5=failure reason
```

## Will Message

Declared when the client connects: "If I disconnect unexpectedly, the broker sends this message": CONNECT will_topic + will_message + will_retain. If the client disconnects abnormally (TCP close or keepalive timeout) → the broker publishes the will message.

## MQTT 5.0 Improvements

1. **Session expiry**: How long the session is retained after disconnection (QoS 1/2 messages in the queue continue)
2. **Message expiry**: TTL for a single message
3. **Shared subscriptions**: Load balancing among multiple subscribers (round-robin)
4. **Reason codes**: All ACK messages include specific reasons
5. **Topic aliases**: 1/2-byte aliases replace full topic names → saves bandwidth

## References

- **broker**: Eclipse Mosquitto (C), EMQX (Erlang), VerneMQ
- **tools**: `mosquitto_pub -h broker -t topic -m "hello"`, MQTT Explorer

*Keywords: MQTT, pub/sub, QoS, retained, will, Mosquitto, IoT, topic filter*
