MQTT 協議其實有用了好久,最近正好有機會對整個協議系統的熟悉以下。主要參考了 MQTT3.1.1 協議中英文的介紹文檔。
mqtt-v3.1.1-errata01-os-complete.pdf
應用消息 Application Message MQTT 協議透過網路傳輸應用數據。應用消息透過 MQTT 傳輸時,它們有關聯的服務質量(QoS)和主題(Topic)。
客戶端 Client:發布消息,訂閱請求。
伺服器 Server:接收、發布消息,處理訂閱。
訂閱包含一個主題過濾器(Topic Filter)和一個最大的服務質量(QoS)等級。
控制報文格式#
MQTT 協議透過交換預定義的 MQTT 控制報文來通信。
控制報文主要包括:固定報頭、可變報頭(部分包含)和有效載荷(部分包含)。
控制報文類型#
| 名字 | 值 | 報文流動方向 | 描述 |
|---|---|---|---|
| Reserved | 0 | 禁止 | 保留 |
| CONNECT | 1 | 客戶端到伺服器 | 客戶端請求連接伺服器 |
| CONNACK | 2 | 伺服器到客戶端 | 連接報文確認 |
| PUBLISH | 3 | 兩個方向都允許 | 發布消息 |
| PUBACK | 4 | 兩個方向都允許 | QoS 1 消息發布收到確認 |
| PUBREC | 5 | 兩個方向都允許 | 發布收到(保證交付第一步) |
| PUBREL | 6 | 兩個方向都允許 | 發布釋放(保證交付第二步) |
| PUBCOMP | 7 | 兩個方向都允許 | QoS 2 消息發布完成(保證交互第三步) |
| SUBSCRIBE | 8 | 客戶端到伺服器 | 客戶端訂閱請求 |
| SUBACK | 9 | 伺服器到客戶端 | 訂閱請求報文確認 |
| UNSUBSCRIBE | 10 | 客戶端到伺服器 | 客戶端取消訂閱請求 |
| UNSUBACK | 11 | 伺服器到客戶端 | 取消訂閱報文確認 |
| PINGREQ | 12 | 客戶端到伺服器 | 心跳請求 |
| PINGRESP | 13 | 伺服器到客戶端 | 心跳響應 |
| DISCONNECT | 14 | 客戶端到伺服器 | 客戶端斷開連接 |
| Reserved | 15 | 禁止 | 保留 |
固定報頭#
固定報頭為兩個字節。
- 第一個字節:高四位為控制報文類型,而低四位為對應控制報文類型的標誌位
- 第二個字節(剩餘長度):表示當前報文剩餘部分的字節數,包括可變報頭和負載的數據。剩餘長度不包括用於編碼剩餘長度字段本身的字節數。
可變報頭#
位於固定報頭與負載之間。很多控制報文的可變報頭部分包含一個兩字節的報文標識符字段。
有效載荷#
位於報文最後。
MQTT 運行流程#
客戶端連接#
客戶端 向 伺服器 發送 connect 報文
包含 客戶端標識符(必須)、用戶名、密碼、協議級別以及遺囑信息等
伺服器 向 客戶端 發送 CONNACK 確認連接請求
訂閱主題#
客戶端向伺服器發送 SUBSCRIBE 報文用於創建一個或多個訂閱(支持通配符 )。SUBSCRIBE 報文也(為每個訂閱)指定了最大的 QoS 等級,伺服器根據這個發送應用消息給客戶端。
SUBACK 用來進行訂閱 確認。<font style="color:rgb(0, 0, 0);">SUBACK</font>報文會為 <font style="color:rgb(0, 0, 0);">SUBSCRIBE</font>報文中的每個主題過濾器返回一個授予的 QoS 等級(這個等級可能等於或小於客戶端請求的最大 QoS,但不會高於它)。
PUBLISH 發送消息#
客戶端使用 PUBLISH 報文發送應用消息給伺服器,目的是分發到其它訂閱匹配的客戶端。
伺服器使用 PUBLISH 報文發送應用消息給每一個訂閱匹配的客戶端。
可變報頭中包含了 主題名 和 報文標識符,有效載荷中包含將要被發布的消息。
- QOS 為 0 時,無確認流程。
- 當 QOS 為 1 時,需要透過 PUBACK 進行發布確認
- 當 QOS 為 2 時,首先是透過 PUBREC 進行發布收到確認,之後使用 PUBREL 進行發布釋放確認,最後使用 PUBCOMP 進行發布完成確認
取消訂閱#
使用 UNSUBSCRIBE 取消訂閱
使用 UNSUBACK 確認收到 UNSUBSCRIBE 報文。
斷開連接#
客戶端發送 DISCONNECT 報文,斷開與伺服器連接。
控制報文#
CONNECT – 連接伺服器#
- 客戶端到伺服器的網路連接建立後,客戶端發送給伺服器的第一個報文必須是 CONNECT 報文
- 在一個網路連接上,客戶端只能發送一次 CONNECT 報文。
- 有效載荷包含一個或多個編碼的字段。包括客戶端的唯一標識符,Will 主題,Will 消息,用戶名和密碼。
固定報頭#
第一字節: 報文類型 0001 低四位作為保留位
剩餘長度等於可變報頭的長度(10 字節)加上有效載荷的長度。
可變報頭#
CONNECT 報文的可變報頭按下列次序包含四個字段:協議名(Protocol Name),協議級別(Protocol Level),連接標誌(Connect Flags)和保持連接(Keep Alive)。
協議名:表示 MQTT 的編碼#
協議級別:表示協議版本#
連接標誌 Connect Flags#

- 清理會話 Clean Session為
為 0 時:伺服器必須基於目前會話(使用客戶端標識符識別)的狀態恢復與客戶端的通信。
為 1 時:斷開之後,舊的會話會被丟棄。
要確保不丟失連接斷開期間的消息,需要使用 QoS 1 或 QoS 2 級別,同時將清理會話標誌設置為 0。
- ** 遺囑標誌:** 伺服器是否需要發布遺囑消息
- ** 遺囑 QOS:** 遺囑消息 QOS 等級
- ** 遺囑保留:** 是否將遺囑消息作為保留消息發布
- ** 用戶名標誌:** 有效載荷中是否包含用戶名
- ** 密碼標誌:** 有效載荷中是否包含密碼
保持連接#
保持連接(Keep Alive)是一個以秒為單位的時間間隔。
指在客戶端傳輸完成一個控制報文的時刻到發送下一個報文的時刻,兩者之間允許空閒的最大時間間隔。期間如果沒有任何其它的控制報文可以發送,客戶端必須發送一個 PINGREQ 報文
如果保持連接的值非零,並且伺服器在一點五倍的保持連接時間內沒有收到客戶端的控制報文,它必須斷開客戶端的網路連接,認為網路連接已斷開。
客戶端發送了 PINGREQ 報文之後,如果在合理的時間內仍沒有收到 PINGRESP 報文,它應該關閉到伺服器的網路連接。
保持連接的值為零表示關閉保持連接功能。
有效載荷#
CONNECT 報文的有效載荷(payload)包含一個或多個以長度為前綴的字段,可變報頭中的標誌決定是否包含這些字段。如果包含的話,必須按這個順序出現:客戶端標識符,遺囑主題,遺囑消息,用戶名,密碼。
客戶端標識符#
伺服器使用客戶端標識符 (ClientId) 識別客戶端。連接伺服器的每個客戶端都有唯一的客戶端標識符(ClientId)。客戶端和伺服器都必須使用 ClientId 識別兩者之間的 MQTT 會話相關的狀態。
客戶端標識符 (ClientId) 必須存在而且必須是 CONNECT 報文有效載荷的第一個字段。
遺囑主題#
如果遺囑標誌被設置為 1,有效載荷中的下一個字段是遺囑主題(Will Topic)。 之後是遺囑消息,根據是否含有標誌包括了用戶名和密碼。
響應#
伺服器可以在同一個 TCP 端口或其他網路端點上支持多種協議 。
伺服器必須發送返回碼為零的 CONNACK 報文作為 CONNECT 報文的確認響應 。但客戶端可以在收到 CONNACK 之前發送控制報文 。
CONNACK – 確認連接請求#
伺服器發送 CONNACK 報文響應從客戶端收到的 CONNECT 報文。伺服器發送給客戶端的第一個報文必 須是 CONNACK 。
固定報頭#

可變報頭#
連接確認標誌#
第 0 (SP) 位 是當前會話(Session Present)標誌。 其他(1-7)位應為0
如果伺服器收到清理會話(CleanSession)標誌為 1 的連接,除了將 CONNACK 報文中的返回碼設置為 0 之外,還必須將 CONNACK 報文中的當前會話設置(Session Present)標誌為 0
當伺服器收到一個 CleanSession 為 0 的連接請求時,需要根據是否已經保存了該客戶端(透過 ClientId 識別)的會話信息來決定 CONNACK 響應中的當前會話標誌。如果之前有保存過這個客戶端的會話信息,那麼在 CONNACK 裡就把當前會話標誌設為 1;如果沒有保存過,則設為 0。另外,不管哪種情況,都要確保 CONNACK 裡的返回碼是 0。
連接返回碼#
一個無符號值,0 表示正常連接;非 0 值則表明連接被拒絕
無有效載荷#
Publish - 發布消息#
固定報頭#

重發標誌 DUP: 0 第一次請求發送,1 表示重發報文.#
發送(出站)的 PUBLISH 報文與收到(入站)的 PUBLISH 報文中的 DUP 標誌是獨立設置的,它的值必須 單獨的根據發送(出站)的 PUBLISH 報文是否是一個重發來確定 .
服務質量等級 QOS#

保留標誌 RETAIN#
如果客戶端發給伺服器的 PUBLISH 報文的保留(RETAIN)標誌被設置為 1,伺服器必須存儲這個應用消 息和它的服務質量等級(QoS),以便它可以被分發給未來的主題名匹配的訂閱者
一個 新的訂閱建立時,對每個匹配的主題名,如果存在最近保留的消息,它必須被發送給這個訂閱者
如果伺服器收到一條保留(RETAIN)標誌為 1 的 QoS 0 消息,它必須丟棄之前為那個主題保留 的任何消息。它應該將這個新的 QoS 0 消息當作那個主題的新保留消息,但是任何時候都可以選擇丟棄它 — 如果這種情況發生了,那個主題將沒有保留消息
伺服器發送PUBLISH 報文給客戶端時,如果消息是作為客戶端一個新訂閱的結果發送,它必須將 ** 報文的保 留標誌設為 1 **
剩餘長度字段#
可變報頭的長度加上有效載荷的長度
可變報頭#
包含主題名和報文標識符。
主題名#
可變報頭的第一個字段
主題名不能包含通配符
報文標識符#
QOS 為 1 或 2 時存在
有效載荷#
有效載荷包含將被發布的應用消息。數據的內容和格式是應用特定的。
響應#
接收者與發布者的 QOS 等級應相同
動作#
客戶端使用 PUBLISH 報文發送應用消息給伺服器,目的是分發到其它訂閱匹配的客戶端。
伺服器使用 PUBLISH 報文發送應用消息給每一個訂閱匹配的客戶端。
客戶端使用帶通配符的主題過濾器請求訂閱時,客戶端的訂閱可能會重複,因此發布的消息可能會匹配多 個過濾器。對於這種情況,伺服器必須將消息分發給所有訂閱匹配的 QoS 等級最高的客戶端 .
伺服器之後可以按照訂閱的 QoS 等級,分發消息的副本給每一個匹配的訂閱者。
PUBACK –發布確認#
QoS 1 等級的 PUBLISH 報文的響應。
固定報頭#
MQTT 報文控制類型為 4
剩餘長度字段為 2
可變報頭#
包含等待確認的 PUBLISH 報文的報文標識符。
無有效載荷#
PUBREC – 發布收到(QoS 2,第一步)#
QoS 等級 2 的 PUBLISH 報文的響應。 它是 QoS 2 等級協議交換的第二個報文。
固定報頭
MQTT 報文控制類型為 5
剩餘長度字段為 2
可變報頭#
包含等待確認的 PUBLISH 報文的報文標識符。
無有效載荷
PUBREL – 發布釋放(QoS 2,第二步) #
對 PUBREC 報文的響應。它是 QoS 2 等級協議交換的第三個報文。
固定報頭#
MQTT 報文控制類型為 5
剩餘長度字段為 2
可變報頭#
與等待確認的 PUBREC 報文相同的報文標識符。
無有效載荷
PUBCOMP – 發布完成(QoS 2,第三步) #
對 PUBREL 報文的響應。它是 QoS 2 等級協議交換的第四個也是最後一個報文。
固定報頭
MQTT 報文控制類型為 7
剩餘長度字段為 2
可變報頭
與等待確認的 PUBREC 報文相同的報文標識符。
無有效載荷
SUBSCRIBE - 訂閱主題 #
客戶端向伺服器發送 SUBSCRIBE 報文用於創建一個或多個訂閱。SUBSCRIBE 報文也 **(為每個訂閱)指定了最大的 QoS 等級 **,伺服器根據這個發送應用消息給客戶端。
固定報頭
MQTT 報文控制類型為 5
剩餘長度字段為 可變報頭 + 有效載荷 的長度
可變報頭#
可變報頭包含客戶端標識符。
有效載荷#
** 主題過濾器 和 QoS 等級字段組合 **
表示客戶端想要訂閱的主題。 伺服器應該 ** 支持 包含通配符(4.7.1 節定義的)的主題過濾器。** 如果伺服器選擇不支持包含通配符的主題過濾器,必須拒絕 任何包含通配符過濾器的訂閱請求 。
每一個過濾器後面跟著一個字節,這個字節被叫做 服 務質量要求(Requested QoS)。它給出了伺服器向客戶端發送應用消息所允許的最大 QoS 等級。 有效載荷必須包含至少一對主題過濾器 和 QoS 等級字段組合。
沒有有效載荷的 SUBSCRIBE 報文是違反協議的 。
響應#
伺服器收到客戶端發送的一個 SUBSCRIBE 報文時,必須使用 SUBACK 報文響應,SUBACK 報文必須和等待確認的 SUBSCRIBE 報文有相同的報文標識符
新訂閱的主題過濾器和之前訂閱的相同,但是它的最大 QoS 值可以不 同。與這個主題過濾器匹配的任何現存的保留消息必須被重發,但是發布流程不能中斷 。
如果伺服器收到包含多個主題過濾器的 SUBSCRIBE 報文,它必須如同收到了一系列的多個 SUBSCRIBE 報文一樣處理那個,除了需要將它們的響應合併到一個單獨的 SUBACK 報文發送 SUBACK 響應可以合併
SUBACK 報文對每一對主題過濾器 和 QoS 等級都必須包含一個返回碼。
伺服器可以授予比 訂閱者要求的低一些的 QoS 等級。為響應訂閱而發出的消息的有效載荷的 QoS 必須是原始發布消息的 QoS 和伺服器授予的 QoS 兩者中的最小值。
** 確定消息分發時可能的最大 QoS 等級是發布者的責任,而訂閱者可以 要求伺服器降低 QoS 到更適合它的等級。 **
SUBACK – 訂閱確認#
用於確認它已收到並且正在處理 SUBSCRIBE 報文。 包含一個返回碼清單,它們指定了 SUBSCRIBE 請求的每個訂閱被授予的最大 QoS 等級。
固定報頭
MQTT 報文控制類型為 5
剩餘長度字段為 可變報頭 + 有效載荷 的長度
可變報頭#
包含等待確認的 SUBSCRIBE 報文的報文標識符。
有效載荷#
包含一個返回碼清單。每個返回碼對應等待確認的 SUBSCRIBE 報文中的一個主題過濾器。返回 碼的順序必須和 SUBSCRIBE 報文中主題過濾器的順序相同
允許的返回碼值:
0x00 - 最大 QoS 0
0x01 - 成功 – 最大 QoS 1
0x02 - 成功 – 最大 QoS 2
0x80 - Failure 失敗
UNSUBSCRIBE –取消訂閱#
用於取消訂閱主題
固定報頭#
控制報文類型 10
保留位 0,0,1,0
** 剩餘長度字段 **可變報頭 + 有效載荷 的長度
可變報頭#
有效載荷#
包含客戶端想要取消訂閱的主體過考列表。
響應#
如果伺服器刪除了一個訂閱:
- 停止分發任何新消息給這個客戶端
- 完成分發任何已經開始往客戶端發送的 QoS 1 和 QoS 2 的消息
- 繼續發送任何現存的準備分發給客戶端的緩存消息。
伺服器必須發送 UNSUBACK 報文響應客戶端的 UNSUBSCRIBE 請求。
UNSUBACK – 取消訂閱確認#
用於確認收到 UNSUBSCRIBE 報文。
固定報頭#
控制報文類型 11
剩餘長度 2
可變報頭#
有效載荷#
無有效載荷
PINGREQ – 心跳請求#
- 告知伺服器客戶端還活著。
- 請求伺服器發送 響應確認它還活著。
- 使用網路以確認網路連接沒有斷開。
固定報頭#
控制報文類型 12
剩餘長度 0
無 可變報頭,有效載荷
響應#
伺服器必須發送 PINGRESP 報文響應客戶端的 PINGREQ 報文
PINGRESP – 心跳響應#
發送 PINGRESP 報文響應客戶端的 PINGREQ 報文。表示伺服器還活著。
固定報頭#
控制報文類型 13
剩餘長度 0
無 可變報頭,有效載荷
DISCONNECT –斷開連接 #
控制報文類型 14
保留位 0
剩餘長度 0 無 可變報頭,有效載荷
響應 #
客戶端發送 DISCONNECT 報文之後:
- 必須關閉網路連接
- 不能透過那個網路連接再發送任何控制報文
伺服器收到 到 DISCONNECT 報文時:
- 必須丟棄任何與當前連接關聯的未發布的遺囑消息
- 應該關閉網路連接,如果客戶端 還沒有這麼做。
操作行為#
MQTT 3.1 使用的傳輸層協議是 [RFC793] 定義的 TCP/IP 協議。同時也支持 TLS、 WebSocket 協議
服務質量等級和協議流程#
QoS 0: 最多分發一次#
消息的分發依賴於底層網路的能力。接收者不會發送響應,發送者也不會重試。
QoS 1: 至少分發一次#
QoS 1 的 PUBLISH 報文的可變報頭中包含一個報文標識符,需要 PUBACK 報文確認。
發送者:
-
每次發送新的應用消息都必須分配一個未使用的報文標識符。
-
發送的 PUBLISH 報文必須包含報文標識符且 QoS 等於 1,DUP 等於 0。
-
必須將這個 PUBLISH 報文看作是 未確認的 ,直到從接收者那收到對應的 PUBACK 報文。
接收者 :
-
響應的 PUBACK 報文必須包含一個報文標識符,這個標識符來自接收到的、已經接受所有權的 PUBLISH 報文。
-
發送了 PUBACK 報文之後,接收者必須將任何包含相同報文標識符的入站 PUBLISH 報文當作一 個新的消息,並忽略它的 DUP 標誌的值。
QoS 2: 僅分發一次#
消息丟失和重複都是不可接受的。
發送者:
- 必須給要發送的新應用消息分配一個未使用的報文標識符。
- 發送的 PUBLISH 報文必須包含報文標識符且報文的 QoS 等於 2,,DUP 等於 0。
- ** 必須將這個 PUBLISH 報文看作是 未確認的 ,直到從接收者那收到對應的 PUBREC 報文。 **
- 收到 PUBREC 報文後必須發送一個 PUBREL 報文。PUBREL 報文必須包含與原始 PUBLISH 報文 相同的報文標識符。
- ** 必須將這個 PUBREL 報文看作是 未確認的 ,直到從接收者那收到對應的 PUBCOMP 報文。 **
- ** 一旦發送了對應的 PUBREL 報文就不能重發這個 PUBLISH 報文。**
響應者:
- 响應的 PUBREC 報文必須包含報文標識符,這個標識符來自接收到的、已經接受所有權的 PUBLISH 報文。
- 在收到對應的 PUBREL 報文之前,接收者必須發送 PUBREC 報文確認任何後續的具有相同標識符 的 PUBLISH 報文。 ** 在這種情況下,它不能重複分發消息給任何後續的接收者。 **
- ** ** 響應 PUBREL 報文的 PUBCOMP 報文必須包含與 PUBREL 報文相同的標識符。
- 發送 PUBCOMP 報文之後,接收者必須將包含相同報文標識符的任何後續 PUBLISH 報文當作一 個新的發布。
消息分發重試#
客戶端和伺服器必須使用原始的報文標識符重發 任何未確認的 PUBLISH 報文(如果 QoS>0)和 PUBREL 報文 。 這是唯一要求客戶端或 伺服器重發消息的情況。
主题名和主題過濾器#
主題通配符#
斜杠(‘/’ U+002F)用於分割主題的每個層級,為主題名提供一個分層結構。
多層通配符#
數字標誌(‘#’ U+0023)是用於匹配主題中任意層級的通配符。