歡迎閱讀 MQTT 5.0 報文系列 的第五篇文章。在上一篇中,我們已經介紹了 MQTT 5.0 的 PINGREQ 和 PINGRESP 報文。現在,我們將介紹下一個控制報文:DISCONNECT。
在 MQTT 中,客戶端和服務端可以在斷開網絡連接前向對端發送一個 DISCONNECT 報文,來指示連接關閉的原因。客戶端發送的 DISCONNECT 報文還可以影響服務端在連接斷開後的行爲,例如是否發送遺囑消息,是否更新會話過期間隔。
DISCONNECT 報文示例
我們使用 MQTTX CLI 向 公共 MQTT 服務器 發起一個指定了 Client ID 的客戶端連接,並將 --reconnect-period
設置爲 0 來禁用自動重連,然後在另一個終端中運行相同的命令創建一個使用相同 Client ID 的連接。
整個過程使用 Wireshark 工具來抓取在客戶端與服務器之間往返的 MQTT 報文,Linux 環境可以使用 tcpdump 命令抓取報文,然後導入至 Wireshark 分析。
以下命令將創建一個 Client ID 爲 mqtt-892324
的客戶端連接,爲了避免 Client ID 與別人重複,建議將它改爲其他隨機字符串:
mqttx conn --hostname broker.emqx.io --mqtt-version 5 --client-id mqtt-892324 \ --reconnect-period 0
在我們發起第二個連接後,Wireshark 將捕獲到公共 MQTT 服務器返回給第一個連接的 DISCONNECT 報文:
e0 02 8e 00
這四個十六進制字節,對應着以下報文內容:
通過下文對 DISCONNECT 報文結構的介紹,你將瞭解到如何從原始的報文數據中提取你想要的信息。
DISCONNECT 報文結構
固定報頭
固定報頭首字節的高 4 位,即報文類型字段的值爲 14(0b1110),低 4 位全部爲 0,表示這是一個 DISCONNECT 報文。
可變報頭
DISCONNECT 報文的可變報頭按順序包含以下字段:
- 原因碼(Reason Code):一個單字節的無符號整數,用於向對端指示連接斷開的原因。下表列出了在 DISCONNECT 報文中常見的 Reason Code,完整列表可參閱 MQTT 5.0 Reason Code 速查表。
Value | Reason Code Name | Sent By | Description |
---|---|---|---|
0x00 | Normal disconnection | 客戶端、服務端 | 表示連接正常關閉,因此服務端不會發布遺囑消息。 |
0x04 | Disconnect with Will Message | 客戶端 | 連接正常關閉,但客戶端希望服務端仍然發佈遺囑消息。 |
0x81 | Malformed Packet | 客戶端、服務端 | 表示收到了無法按照協議規範正確解析的控制報文,在 MQTT 中我們將這類報文稱爲畸形報文。 |
0x82 | Protocol Error | 客戶端、服務端 | 協議錯誤通常指控制報文在按照協議規範解析以後才能發現的錯誤,包括包含協議不允許的數據、行爲與協議要求不符等等。比如客戶端在一個連接內發送了兩個 CONNECT 報文。 |
0x8D | Keep Alive timeout | 服務端 | 服務端在超過 1.5 倍的 Keep Alive 時間內沒有收到任何報文,因此關閉了連接。 |
0x8E | Session taken over | 服務端 | 另一個更新的使用了且相同的 Client ID 的連接被建立,導致服務端關閉了此連接。 |
0x93 | Receive Maximum exceeded | 客戶端、服務端 | 表示對端同時發送的 QoS > 0 的 PUBLISH 報文數量超過了連接時設置的接收最大值。 |
0x94 | Topic Alias invalid | 客戶端、服務端 | 表示主題別名不合法。比如 PUBLISH 報文中的主題別名值爲 0 或者大於連接時約定的最大主題別名。 |
0x95 | Packet too large | 客戶端、服務端 | 表示報文超過了連接時約定的最大允許長度。 |
0x98 | Administrative action | 客戶端、服務端 | 表示連接因爲管理操作而被關閉,比如運維人員在服務端後臺踢除了客戶端連接。 |
0x9C | Use another server | 服務端 | 表示客戶端應該臨時切換到另一個服務器。如果另一個服務器不是客戶端已知的,那麼還需要配合 Server Reference 屬性一起使用,以告知客戶端新的服務端的地址。 |
0x9D | Server moved | 服務端 | 表示客戶端應該永久切換到另一個服務器。如果另一個服務器不是客戶端已知的,那麼還需要配合 Server Reference 屬性一起使用,以告知客戶端新的服務端的地址。 |
- 屬性(Properties):下表列出了 DISCONNECT 報文的所有可用屬性。
Identifier | Property Name | Sent By | Type |
---|---|---|---|
0x11 | Session Expiry Interval | 客戶端 | 四字節整數 |
0x1F | Reason String | 客戶端、服務端 | UTF-8 編碼的字符串 |
0x26 | User Property | 客戶端、服務端 | UTF-8 字符串對 |
0x1C | Server Reference | 服務端 | UTF-8 編碼的字符串 |
與之前介紹的其他報文不同,客戶端和服務端在 DISCONNECT 報文中可以使用的原因碼和屬性是不同的,例如 Session Expiry Interval 屬性就只能在客戶端發送的 DISCONNECT 報文中使用,所以我們在上面的列表中均列出了它們的可用範圍。
有效載荷
DISCONNECT 報文不包含有效載荷。
總結
客戶端和服務端都可以發送 DISCONNECT 報文,表示準備斷開網絡連接,報文中的原因碼可以向接收方指示連接關閉的原因。當 MQTT 連接意外斷開時,我們可以優先查看是否收到了 DISCONNECT 報文以及報文中原因碼的值。
雖然客戶端和服務端在 DISCONNECT 報文中可以用的原因碼和屬性存在差異,但我們並不需要強行去記憶它們。它們通常都和對應的機制與行爲相關,例如遺囑消息只會由服務端發佈,所以希望連接正常關閉但對端仍要發佈遺囑消息的原因碼 0x04,也會被客戶端使用。
以上就是對 DISCONNECT 報文的介紹,在下一篇文章中我們將介紹 MQTT 5.0 增強認證特性所使用的 AUTH 報文,它也是 MQTT 中最後一個報文類型。