在 MQTT 5.0 報文介紹 中,我們介紹了 MQTT 報文由固定報頭、可變報頭和有效載荷三個部分組成,以及可變字節整數、屬性這類 MQTT 報文中的通用概念。現在,我們將按照實際的用途來進一步介紹各個類型的報文的組成。首先,我們將專注於用於建立 MQTT 連接的報文。
如果我們想要使用 MQTT 進行通信,第一步必然是建立一個 MQTT 連接,而建立 MQTT 連接需要用到兩個控制報文,它們分別是 CONNECT 報文與 CONNACK 報文。CONNECT 報文是客戶端與服務端建立網絡連接後,向服務端發送的第一個控制報文,用來發起連接請求。服務端將返回 CONNACK 報文告知客戶端連接結果。
報文示例
我們使用 MQTTX CLI 向 公共 MQTT 服務器 發起一個連接,在這個連接中,我們將協議版本設置 MQTT 5.0,Clean Start 設置爲 1,Session Expiry Interval 設置爲 300 秒,Keep Alive 設置爲 60,用戶名和密碼分別設置爲 admin 和 public,對應的 MQTTX CLI 命令爲:
mqttx conn --hostname broker.emqx.io --mqtt-version 5 \ --session-expiry-interval 300 --keepalive 60 --username admin --password public
以下是使用 Wireshark 工具抓取到的 MQTTX CLI 發出的 CONNECT 報文,Linux 環境可以先使用 tcpdump 命令抓取報文,然後再導入至 Wireshark 查看:
10 2f 00 04 4d 51 54 54 05 c2 00 3c 05 11 00 00 01 2c 00 0e 6d 71 74 74 78 5f 30 63 36 36 38 64 30 64 00 05 61 64 6d 69 6e 00 06 70 75 62 6c 69 63
但這是一串不易理解的十六進制字節,除非它們被轉換成以下格式:
同樣我們也抓取到了公共 MQTT 服務器返回的 CONNACK 報文:
20 13 00 00 10 27 00 10 00 00 25 01 2a 01 29 01 22 ff ff 28 01
在解析這串報文數據之後我們可以看到,CONNACK 報文的 Reason Code 爲 0,表示連接成功,後面的多個屬性則給出了服務器支持的功能列表,比如支持的最大報文長度,是否支持保留消息等等:
當然,Wireshark 其實也已經爲我們列出了報文中各個字段的值,通過下文對 CONNECT 和 CONNACK 報文結構的介紹,再結合 Wireshark 的抓包結果按圖索驥,你將很快掌握這兩個報文:
CONNECT 報文結構
固定報頭
CONNECT 報文的固定報頭中,位於首字節高 4 位的報文類型字段的值必須爲 1(0b0001),首字節中低 4 位則固定全爲 0。
所以,CONNECT 報文的第一個字節的值必然爲 0x10
,我們可以以此來判斷某個報文是否爲 CONNECT 報文。
可變報頭
CONNECT 報文的可變報頭按順序包含以下字段:
-
Protocol Name:這是一個 UTF-8 編碼的字符串,用來表示協議名稱。在 MQTT 中,UTF-8 編碼的字符串的前兩個字節統一用於指示後面實際的字符數據的長度。MQTT 3.1.1 和 MQTT 5.0 中協議名稱固定爲
MQTT
,所以對應的以十六進制字節表示的完整內容就是00 04 4d 51 54 54
,其中4d 51 54 54
就是MQTT
這個字符串對應的 ASCII 值。最早的 MQTT 3.1 中的協議名稱是MQIsdp
,所以它對應的是00 06 4d 51 49 73 64 70
。 -
Protocol Version:這是一個單個字節長度的無符號整數,用來表示協議版本。目前只有三個可取值,3 表示 MQTT 3.1,4 表示 MQTT 3.1.1,5 表示 MQTT 5.0。
-
Connect Flags:連接標識,它只有一個字節,但包含了多個用於控制連接行爲或指示有效載荷中某些字段是否存在的參數。
-
User Name Flag:用於指示有效載荷是否包含用戶名字段。
-
Password Flag:用於指示有效載荷是否包含密碼字段。
-
Will Retain:用於指示遺囑消息是否爲保留消息。
-
Will QoS:用於指示遺囑消息的 QoS。
-
Will Flag:用於指示有效載荷是否包含了遺囑消息的相關字段。
-
Clean Start:用於指示當前連接是一個新的會話還是一個已存在會話的延續,這決定了服務端將直接新建會話還是嘗試複用已存在的會話。
-
Reserved:這是一個保留位,它的值必須爲 0。
-
-
Keep Alive:這是一個雙字節長度的無符號整數,用來表示客戶端發送兩個相鄰的控制報文的最大時間間隔。
-
Properties:下表列出了 CONNECT 報文的所有可用屬性。
Identifier | Property Name | Type |
---|---|---|
0x11 | Session Expiry Interval | 四字節整數 |
0x21 | Receive Maximum | 雙字節整數 |
0x27 | Maximum Packet Size | 四字節整數 |
0x22 | Topic Alias Maximum | 雙字節整數 |
0x19 | Request Response Information | 單字節 |
0x17 | Request Problem Information | 單字節 |
0x26 | User Property | UTF-8 字符串對 |
0x15 | Authentication Method | UTF-8 編碼的字符串 |
0x16 | Authentication Data | 二進制數據 |
有效載荷
CONNECT 報文有效載荷中的字段,除了 Client ID 以外,其他字段都是可選的,它們是否存在取決於可變報頭的 Connect Flags 中對應標誌位的值。但如果這些存在,就必須按照 Client ID、Will Properties、Will Topic、Will Payload、User Name、Password 的順序出現。
CONNACK 報文結構
固定報文
固定報頭中首字節的高 4 位值爲 2(0b0010),表示這是一個 CONNACK 報文。
可變報頭
CONNACK 報文的可變報頭按順序包含以下字段:
-
Connect Acknowledge Flags:連接確認標誌。
-
Reserved (Bit 7 - 1):保留位,必須設置爲 0.
-
Session Present (Bit 0):用於指示服務端是否正在使用已存在的會話與客戶端恢復通信。僅在客戶端在 CONNECT 連接中將 Clean Start 設置爲 0 時,Session Present 可能爲 1。
-
-
Reason Code:用於指示連接結果。下表列出了一些在 CONNACK 報文中常見的 Reason Code,完整列表可參閱 MQTT 5.0 Reason Code 速查表。
Value | Reason Code Name | Description |
---|---|---|
0x00 | Success | 連接被接受。 |
0x81 | Malformed Packet | 服務端無法按照協議規範正確解析 CONNECT 報文,例如保留位沒有按照協議要求設置爲 0。 |
0x82 | Protocol Error | CONNECT 報文可以被正確解析,但是內容不符合協議規範,比如 Will Topic 字段的值不是一個合法的 MQTT 主題。 |
0x84 | Unsupported Protocol Version | 服務端不支持客戶端所請求的 MQTT 協議版本。 |
0x85 | Client Identifier not valid | 表示 Client ID 是有效的字符串,但是不被服務端接受,比如 Client ID 超出了服務端允許的最大長度。 |
0x86 | Bad User Name or Password | 客戶端因爲使用了錯誤的用戶名或密碼而被拒絕連接。 |
0x95 | Packet too large | CONNECT 報文超過了服務端允許的最大長度,可能是因爲攜帶了較大的遺囑消息。 |
0x8A | Banned | 表示客戶端被禁止登錄。例如服務端檢測到客戶端的異常連接行爲,所以將這個客戶端的 Client ID 或者 IP 地址加入到了黑名單列表中,又或者是後臺管理人員手動封禁了這個客戶端,當然以上這些通常需要視服務端的具體實現而定。 |
- Properties:下表列出了 CONNACK 報文的所有可用屬性。
Identifier | Property Name | Type |
---|---|---|
0x11 | Session Expiry Interval | 四字節整數 |
0x21 | Receive Maximum | 雙字節整數 |
0x24 | Maximum QoS | 單字節 |
0x25 | Retain Available | 單字節 |
0x27 | Maximum Packet Size | 四字節整數 |
0x12 | Assigned Client Identifier | UTF-8 編碼的字符串 |
0x22 | Topic Alias Maximum | 雙字節整數 |
0x1F | Reason String | UTF-8 編碼的字符串 |
0x26 | User Property | UTF-8 字符串對 |
0x28 | Wildcard Subscription Available | 單字節 |
0x29 | Subscription Identifier Available | 單字節 |
0x2A | Shared Subscription Available | 單字節 |
0x13 | Server Keep Alive | 雙字節整數 |
0x1A | Response Information | UTF-8 編碼的字符串 |
0x1C | Server Reference | UTF-8 編碼的字符串 |
0x15 | Authentication Method | UTF-8 編碼的字符串 |
0x16 | Authentication Data | 二進制數據 |
有效載荷
CONNACK 報文不包含有效載荷。
總結
CONNECT 是客戶端與服務端的網絡連接建立後,客戶端發送的第一個 MQTT 報文,CONNACK 作爲 CONNECT 的響應報文通過原因碼來指示連接結果。
客戶端和服務端需要藉助 CONNECT 和 CONNACK 報文來完成必要信息的交換,例如客戶端使用的協議版本、Client ID、用戶名、密碼,以及服務端是否存在相應的會話、支持的最大報文長度和最大 QoS 等級等等。
以上就是對 MQTT CONNECT 和 CONNACK 報文的介紹,在後續的文章中,我們還會繼續研究 PUBLISH、DISCONNECT 這些報文的結構和組成。