---
title: MQTT
url: https://doc.liz6.com/networking/10-real-time-communication/03-MQTT
locale: zh
area: networking
tags:
- networking
- 实时通信
date: 2026-06-30
modified: 2026-07-11
description: 为低带宽、高延迟、不可靠的 IoT 网络设计的最简洁 pub/sub 协议——QoS 0/1/2 提供三级递送保证,retained message 实现状态同步,will message 处理离线通知。MQTT 5.0 增加了 session expiry 和 shared subscriptions 等企业特性。
---

# MQTT

> 为低带宽、高延迟、不可靠的 IoT 网络设计的最简洁 pub/sub 协议——QoS 0/1/2 提供三级递送保证,retained message 实现状态同步,will message 处理离线通知。MQTT 5.0 增加了 session expiry 和 shared subscriptions 等企业特性。

## 概述

MQTT（Message Queuing Telemetry Transport, 1999/2013）是最简洁的 pub/sub 消息协议，专门为低带宽、高延迟、不可靠的 IoT 网络设计。发布者不直接连订阅者——所有消息经过 broker（Mosquitto/EMQX），topic 支持通配符过滤。QoS 0/1/2 提供三种递送保证，retained message 和 will message 实现状态同步和离线通知。MQTT 5.0（2019）增加了 session expiry、shared subscriptions 等企业特性。

## 模型

MQTT 是极简的 pub/sub 协议，专为低带宽、高延迟、不可靠网络设计 (IoT, M2M)。所有消息经过 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 发布订阅模型:消息经 Broker 按 topic 过滤分发给匹配的订阅者">
  <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">发布/订阅模型:消息经 Broker 按 topic 过滤分发</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">温度传感器</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">→ 推送给所有匹配的订阅者</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">精确匹配</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">单层通配 +</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">多层通配 #(含所有子层)</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">Broker 只做 topic 字符串匹配、不关心消息内容;+ 通配单层,# 通配多层及以下所有子层。</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 三级递送保证:握手次数换来的可靠性">
  <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:握手次数换来的递送保证</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">最多一次</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,不确认</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,不确认</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">不确认 · 不重试</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">至少一次</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 · 确认</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">可能重复</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">恰好一次</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">不重复不丢失</text>
  <text x="616" y="250" text-anchor="middle" font-size="8.5" fill="#15803d">四次握手确认</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">QoS 数字越大、握手次数越多,递送越可靠,但延迟和带宽开销也越高——</text>
  <text x="46" y="337" font-size="12.5" fill="#3730a3">MQTT 把选择权交给每条消息,不是全局一刀切。</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: 秒 (publisher 在 keep_alive 秒内必须发 PINGREQ)
  client_id: 唯一 ID (或空 → broker 分配, 仅 v5.0)
  [will_topic, will_message] ← 如果 will_flag=1
  [username, password]

CONNACK:
  session_present: 0 (新 session), 1 (已有 session 恢复)
  return_code: 0=success, 1-5=失败原因
```

## Will Message

客户端连接时声明: "如果我意外断开, broker 发这条消息": CONNECT will_topic + will_message + will_retain。客户端异常断开 (TCP close 或 keepalive timeout) → broker 发布 will message。

## MQTT 5.0 改进

1. **Session expiry**: 断开后 session 保留多久 (队列中的 QoS 1/2 消息继续)
2. **Message expiry**: 单条消息 TTL
3. **Shared subscriptions**: 多个 subscriber 负载均衡 (round-robin)
4. **Reason codes**: 所有 ACK 消息含具体原因
5. **Topic aliases**: 1/2-byte alias 替完整 topic → 省带宽

## 参考

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

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