MQTT 3
MQTT 3 (當前版本3.1.1)是目前使用的最為廣泛的MQTT協議標准。盡管MQTT5標准已經發布,並且帶來了一些令人振奮的新特性,但是在整個應用場景上,從后台服務到消息中間件再到客戶端SDK等環節上的產品升級並沒有都完成,再加上既有部署的維護,業界從版本3到5的過渡可能會持續相當長一段時間,所以,對於剛加入物聯網行業的生力軍來說,現在來學習MQTT 3依然是一件很有意義的事情。
MQTT協議的工作方式
在一個QMTT協議中有三個角色會參與到整個通信過程,發布者(publisher)、代理(broker)和訂閱者(subscriber)。有別於傳統的客戶端/服務器通訊協議,MQTT協議並不是端到端的,消息傳遞通過代理,包括會話(session)也不是建立在發布者和訂閱者之間,而是建立在端和代理之間。代理解除了發布者和訂閱者之間的耦合。
除了發布者和訂閱者之間傳遞普通消息,代理還可以為發布者處理保留消息和遺願消息,並可以更改服務質量(QoS)等級。
MQTT控制報文
MQTT協議工作在TCP之上,端和代理之間通過交換預先定義的控制報文來完成通信。MQTT報文有3個部分組成,並按下表順序出現:
固定報頭(fixed header) | 可變報頭(variable header) | 荷載(payload) |
---|---|---|
所有報文都包含 | 部分報文包含 | 部分報文包含 |
所有的MQTT控制報文都有一個固定報頭,期格式如下:

協議版本3定義了14種MQTT報文,用於建立/斷開連接、發布消息、訂閱消息和維護連接。固定報頭的第一字節的4-7位的值指定了報文類型,其取值如下表。0和15為系統保留值;0-3位為標志位,依照報文類型有不同的含義,事實上,除了PUBLISH報文以外,其他報文的標志位均為系統保留。如果收到報文的標志位無效,代理應斷開連接。
報文類型 | 值 | 描述 |
---|---|---|
CONNECT | 1 | 客戶端向代理發起連接請求 |
CONNACK | 2 | 連接確認 |
PUBLISH | 3 | 發布消息 |
PUBACK | 4 | 發布確認 |
PUBREC | 5 | 發布收到(QoS2) |
PUBREL | 6 | 發布釋放(QoS2) |
PUBCOMP | 7 | 發布完成(QoS2) |
SUBSCRIBE | 8 | 客戶端向代理發起訂閱請求 |
SUBACK | 9 | 訂閱確認 |
UNSUBSCRIBE | 10 | 取消訂閱 |
UNSUBACK | 11 | 取消訂閱確認 |
PINGREQ | 12 | PING請求 |
PINGRESP | 13 | PING響應 |
DISCONNECT | 14 | 斷開連接 |
固定報頭的第二字節起表示報文的剩余長度。最大4個字節,每字節可以編碼至127,並含有一位繼續位,如繼續位非0,則下一字節依然為剩余長度。由此,理論上一個控制報文最長可以到256MB。
一些報文在固定報頭和荷載之間可以有一個可變報頭。可變報頭的內容根據報文類型不同而不同。最常見的可變報頭是報文標識符(PacketIdentifier)。
一些報文可以在最后攜帶一個荷載。不同的報文可以無荷載,可選荷載,或必須帶有荷載。
CONNECT報文
CONNECT是客戶端連接到代理的第一個報文,如果在連接已經存在,代理收到該報文將會斷開現有連接。
CONNECT報文的固定報頭
CONNECT報文的可變報頭
CONNECT報文的可變報頭由4部分組成:
- 協議名。協議名是UTF-8編碼的大寫的MQTT。
- 協議級別。MQTT 3.1.1的協議級別為4.
- 連接標志位。定義連接行為的參數。見下表。
- Keep Alive。2字節,客戶端和代理之間的無活動時間超過該值后,應關閉連接。如果該值置0表示客戶端不要求代理啟用KEEPALIVE功能。
連接標志位:
位 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
---|---|---|---|---|---|---|---|---|
用戶名 | 密碼 | 保留遺願 | 遺願QoS | 遺願QoS | 遺願 | 清除會話 | 保留(0) |
清除會話標志位:
這個標志位定義了如何處理會話狀態。如果設置為0,客戶端和代理可以恢復上一次連接時的會話狀態,如果上一次連接的會話狀態不存在,代理將會為客戶端建立一個新的會話。如果該位設置為1,則雙方將清除掉上一次連接的會話狀態並建立一個新的會話。
遺願標志位:
如果遺願標志為1,則遺願消息會被存儲在代理上,當連接關閉時,代理將發布這個消息,除非在客戶端斷開連接時把遺願消息清除了。
遺願QoS標志位:
指定了遺願消息的服務質量等級。
保留遺願消息標志位:
指定在發布遺願消息的時候,是否把該消息作為保留消息存儲在代理。
用戶名標志位:
如果設置為1,則用戶名必須出現在荷載中,反之,用戶名不允許出現在荷載中。
密碼標志位:
如果該位為1,則密碼必須出現在荷載中;如果該位為0,則密碼不允許出現在荷載中。如果用戶名標志位為0,則該位必須也為0。
CONNECT報文的荷載
CONNECT報文的荷載由一個或者多個字段組成,這些字段是否出現由可變報頭中的標志位決定。字段總是以長度開始。字段出現的順序必須是:客戶端標識符,遺願主題,遺願消息,用戶名,密碼。
CONNECT報文的響應
在代理在為MQTT協議開放的端口上接收到TCP連接請求並建立連接后應該會收到CONNECT報文,如果在一定時間內代理沒有收到CONNECT報文,則應該關閉這個TCP連接。
在收到CONNECT報文后,代理應該檢查報文格式是否符合協議標准。如果不符合協議標准,代理應關閉連接,且不發送CONNACK報文給客戶端。
代理可以檢查CONNECT報文的內容並執行響應的認證和鑒權。如果這些檢查沒有通過,代理應該向客戶端發送一個帶有非0返回碼的CONNACK報文。
CONNACK報文
CONNACK是代理用來響應客戶端CONNECT的報文。代理向客戶端發送的第一個報文必須是CONNACT。CONNACK有一個固定報頭,一個可變報頭,但是不帶有荷載。
CONNACK的固定報頭
CONNACT報文只有固定報頭和一個2字節的可變報頭,所以它的剩余長度總是2。
CONNACK報文的可變報頭
CONNACK報文的可變報頭為定長2字節。第一字節的0位表示是否有會話存在。如果代理上已經有請求連接的客戶端的會話,且連接請求的清除會話標識為0,則該位為1,否則該位為0。客戶端可以根據這一位的值采取響應行為,比如(重新)訂閱主題等。
CONNACK報文的可變報頭的第二字節為返回碼。如果CONNECT請求的格式正確,但是代理依然不能允許客戶端連接,則返回碼為一個非零值。如果連接成功,則返回0。
返回碼的定義:
值 | 返回碼含義 |
---|---|
0 | 成功,連接請求被接受。 |
1 | 拒絕連接,不可接受的協議版本。 |
2 | 拒絕連接,不被允許的身份識別符(Client Identifier)。 |
3 | 拒絕連接,服務器不可用。 |
4 | 拒絕連接,無效的用戶名和密碼。 |
5 | 拒絕連接,客戶端無授權。 |
6-255 | 系統保留。 |
客戶端接受到代理的CONNACK的返回碼為0,則連接建立完成,雙方可以開始通信。
清除會話、保留消息和QoS的組合
清除會話、保留消息等概念,在傳統的客戶端/服務器方式的通信中不一定會出現,這些概念有時候不太容易理解,特別是當他們被組合起來用的時候。
下面的表格匯總了當一個客戶端連接上來時,它能收到消息的各種情況。
清除會話位 | 保留位 | 訂閱QoS | 發布QoS | 可收到的消息 |
---|---|---|---|---|
Y | N | 0 | 0 | N |
Y | N | 0 | 1 | N |
Y | N | 1 | 0 | N |
Y | N | 1 | 1 | N |
N | N | 0 | 0 | N |
N | N | 0 | 1 | N |
N | N | 1 | 0 | N |
N | N | 1 | 1 | Y,會話全部消息 |
Y | Y | 0 | 0 | Y,最后一條消息 |
Y | Y | 0 | 1 | Y,最后一條消息 |
Y | Y | 1 | 0 | Y,最后一條消息 |
Y | Y | 1 | 1 | Y,最后一條消息 |
N | Y | 0 | 0 | Y,最后一條消息 |
N | Y | 0 | 1 | Y,最后一條消息 |
N | Y | 1 | 0 | Y,最后一條消息 |
N | Y | 1 | 1 | Y,會話全部消息 |
MQTT 5.0 協議新增介紹
MQTT 5.0 協議相比 MQTT 3.1.1 協議新增了許多內容, 比如說屬性,AUTH 包,還有對一些字段做了修改,比如將 Clean Session 修改成 Clean Start 配合 Session Expiry Internal 去實現更靈活的會話控制。
這里就簡單羅列一下 5.0 協議新增的內容。
設計目標
- 增強了擴展性
- 改善了錯誤報告的方式
- 定型了一些通用范式,例如能力發現和請求、響應
- 擴展機制包括用戶屬性(user properties)
- 性能改善,並且添加了對小客戶端(small clients) 的支持
屬性
為了達成新協議的設計目標,MQTT 5.0 協議中新增了許多屬性,以下是新添加的屬性列表。
標識符 Identifier(十進制) | 標識符 Identifier(十六進制) | 名稱(用法)Name(usage) | 類型 Type | 報文/遺囑屬性 Packet/Will Properties |
---|---|---|---|---|
1 | 0x01 | 有效載荷格式指示器 Payload Format Indicator | 字節 | PUBLISH, 遺囑屬性 Will Properties |
2 | 0x02 | 消息到期間隔 Message Exipiry Interval | 四字節整形 | PUBLISH,遺囑屬性 |
3 | 0x03 | 內容類型 Content Type | UTF-8 編碼字符串 | PUBLISH,遺囑屬性 |
8 | 0x08 | 響應主題 Response Topic | UTF-8 編碼字符串 | PUBLISH,遺囑屬性 |
9 | 0x09 | 關聯數據 Correlation Data | 二進制數據 Binary Data | PUBLISH,遺囑屬性 |
11 | 0x0B | 訂閱標識符 Subscription Identifier | 可變字節整形 | PUBLISH, SUBSCRIBE |
17 | 0x11 | 會話到期間隔 Session Expiry Interval | 四字節整形 | CONNECT, CONNACK, DISCONNECT |
18 | 0x12 | 已分配客戶端標識符 Assigned Client Identifier | UTF-8 編碼字符串 | CONNACK |
19 | 0x13 | 服務器保活 Server Keep Alive | 兩字節整形 | CONNACK |
21 | 0x15 | 認證方法 Authentication Method | UTF-8 編碼字符串 | CONNECT, CONNACK, AUTH |
22 | 0x16 | 認證數據 Authentication Data | 二進制數據 | CONNECT, CONNACK, AUTH |
23 | 0x17 | 請求響應信息 Request Response Information | 字節 | CONNECT |
24 | 0x18 | 遺囑延遲間隔 Will Delay Interval | 四字節整形 | 遺囑屬性 Will Properties |
25 | 0x19 | 請求響應信息 Request Response Information | 字節 | CONNECT |
26 | 0x1A | 響應信息 Response Information | UTF-8 編碼字符串 | CONNACK, DISCONNECT |
28 | 0x1C | 服務器引用 Server Reference | UTF-8 編碼字符串 | CONNACK, DISCONNECT |
31 | 0x1F | 原因字符串 Reason String | UTF-8 編碼字符串 | CONNACK, PUBACK, PUBREC, PUBREL, PUBCOMP, SUBACK,UNSUBACK, DISCONNECT, AUTH |
33 | 0x21 | 接收最大值 Receive Maximum | 兩字節整形 | CONNECT, CONNACK |
34 | 0x22 | 主題別名最大值 Topic Alias Maximum | 兩字節整形 | CONNECT, CONNACK |
35 | 0x23 | 主題別名 Topic Alias | 兩字節整形 | PUBLISH |
36 | 0x24 | 服務質量最大值 Maximum QoS | 字節 | CONNACK |
37 | 0x25 | 保留可用 Retain Available | 字節 | CONNACK |
38 | 0x26 | 用戶屬性 User Property | UTF-8 字符串對 UTF-8 String Pair | CONNECT, CONNACK, PUBLISH, PUBACK, PUBREC, PUBREL, PUBCOMP, SUBACK, UNSUBACK, DISCONNECT, AUTH |
39 | 0x27 | 最大報文大小 Maximum Packet Size | 四字節整形 | CONNECT, CONNACK |
40 | 0x28 | 可用通配符訂閱 Wildcard Subscription Available | 字節 | CONNACK |
41 | 0x29 | 可用訂閱標識符 Subscription Identifier Available | Byte | CONNACK |
42 | 0x2A | 可用共享訂閱 Shared Subscription Available | 字節 | CONNACK |
原因碼
MQTT v3.1.1 只有寥寥 6 個返回碼,用來表示網絡連接時可能會出現的異常行為,在引入屬性后的 MQTT 5.0 協議中,僅僅這 6 個返回碼顯然已經不足以用來描述各種異常行為,因此MQTT 5.0 協議中將返回碼改成了原因碼,用來實現改善錯誤報告的目的。
原因碼(十進制) | 原因碼(十六進制) | 名稱 | 報文 |
---|---|---|---|
0 | 0x00 | 成功 Success | CONNACK, PUBACK, PUBREC, PUBREL, PUBCOMP, UNSUBACK, AUTH |
0 | 0x00 | 正常斷連 Normal disconnection | DISCONNECT |
0 | 0x00 | 准許 QoS 0 Granted QoS 0 | SUBACK |
1 | 0x01 | 准許 QoS 1 Granted QoS 1 | SUBACK |
2 | 0x02 | 准許 QoS 2 Granted QoS 2 | SUBACK |
4 | 0x04 | 以遺囑消息斷開連接 Disconnect with Will Message | DISCONNECT |
16 | 0x10 | 沒有匹配的訂閱者 No matching subscribers | PUBACK, PUBREC |
17 | 0x11 | 沒有訂閱 No subscription existed | UNSUBACK |
24 | 0x18 | 繼續認證 Continue authentication | AUTH |
25 | 0x19 | 重新認證 Re-authenticate | AUTH |
128 | 0x80 | 未指定錯誤 Unspecified error | CONNACK, PUBACK, PUBREC, SUBACK, UNSUBACK, DISCONNECT |
129 | 0x81 | 畸形報文 Malformed Packet | CONNACK, DISCONNECT |
130 | 0x82 | 協議錯誤 Protocol Error | CONNACK, DISCONNECT |
131 | 0x83 | 實現特有錯誤 Implementation specific error | CONNACK, PUBACK, PUBREC, SUBACK, UNSUBACK, DISCONNECT |
132 | 0x84 | 不支持的協議版本 Unsupported Protocol Version | CONNACK |
133 | 0x85 | 客戶端標識符無效 Client Identifier not valid | CONNACK |
134 | 0x86 | 錯誤的用戶名和密碼 Bad User Name or Password | CONNACK |
135 | 0x87 | 未授權 Not authorized | CONNACK, PUBACK, PUBREC, SUBACK, UNSUBACK, DISCONNECT |
136 | 0x88 | 服務器不可用 Server unavailable | CONNACK |
137 | 0x89 | 服務器繁忙 Server busy | CONNACK, DISCONNECT |
138 | 0x8A | 禁止訪問 Banned | CONNACK |
139 | 0x8B | 服務器關機中 Server shutting down | DISCONNECT |
140 | 0x8C | 錯誤驗證方法 Bad authentication method | CONNACK, DISCONNECT |
141 | 0x8D | 保活超時 Keep Alive timeout | DISCONNECT |
142 | 0x8E | 會話被接管 Session taken over | DISCONNECT |
143 | 0x8F | 主題過濾器無效 Topic Filter invalid | SUBACK, UNSUBACK, DISCONNECT |
144 | 0x90 | 主題名無效 Topic Name invalid | CONNACK, PUBACK, PUBREC, DISCONNECT |
145 | 0x91 | 報文標識符在使用中 Packet Identifier in use | PUBACK, PUBREC, SUBACK, UNSUBACK= |
146 | 0x92 | 沒有發現報文標識符 Packet Identifier not found | PUBREL, PUBCOMP |
147 | 0x93 | 超出接收最大值 Receive Maximum exceeded | DISCONNECT |
148 | 0x94 | 主題別名無效 Topic Alias invalid | DISCONNECT |
149 | 0x95 | 報文太大 Packet too large | CONNACK, DISCONNECT |
150 | 0x96 | 消息傳輸速率太高 Message rate too high | DISCONNECT |
151 | 0x97 | 超出限額 Quota exceeded | CONNACK, PUBACK, PUBREC, SUBACK, DISCONNECT |
152 | 0x98 | 管理行為 Administrative action | DISCONNECT |
153 | 0x99 | 有效載荷格式無效 Payload format invalid | PUBACK, PUBREC, DISCONNECT |
154 | 0x9A | 不支持消息保留 Retain not supported | CONNACK, DISCONNECT |
155 | 0x9B | 不支持的QoS QoS not supported | CONNACK, DISCONNECT |
156 | 0x9C | 使用另一台服務器 Use another server | CONNACK, DISCONNECT |
157 | 0x9D | 服務器被移除 Server moved | CONNACK, DISCONNECT |
158 | 0x9E | 不支持的共享訂閱 Shared Subscription not supported | SUBACK, DISCONNECT |
159 | 0x9F | 超出連接速率 Connection rate exceeded | CONNACK, DISCONNECT |
160 | 0xA0 | 最大連接時間 Maximum connect time | DISCONNECT |
161 | 0xA1 | 不支持的訂閱標識符 Subscription Identifiers not supported | SUBACK, DISCONNECT |
162 | 0xA2 | 不支持的通配符訂閱 Wildcard Subscription not supported | SUBACK, DISCONNECT |
實際應用
1. 由於主題別名(Topic Alias)的引入,使 MQTT PUBLISH 控制報文的體積更小,更便於在帶寬和網絡受限的物聯網環境下傳輸消息。
2. AUTH 包的引入使 MQTT 協議擴展了認證方式,增加了詢問/響應式的認證方式,服務器或客戶端在發送 CONNECT 與接收 CONNACK 包之間交換 AUTH 報文來完成身份驗證的流程。
3. 由於很多嵌入式設備的 CPU 並沒有對 AES 加密標准下的加密算法提供硬件級的支持,因此,使用 AES 加密對嵌入式設備的硬件開銷是非常大的,所以 MQTT 5.0 協議提供了新的加密算法 ChaCha20 ,ChaCha20 在軟件層面做加密和解密處理要比 AES 快得多。因此也算是一大進步,不過本人更希望 MQTT 下一版協議能夠增加對 AEAD 加密算的支持。
總的來說,MQTT 5.0 協議的內容增加了很多,協議書的內容幾乎是 MQTT 3.1.1 協議的兩倍。