MQTT協議探究(二)
阿新 • • 發佈:2018-11-09
1 回顧與本次目標
1.1 回顧
- MQTT控制報文的基本格式
- WireShark進行抓包分析了報文
- 報文分析:
- CONNECT——連線伺服器
- CONNACK——確認連線請求
- PINGREQ——心跳請求
- PINGRESP——心跳響應
- DISCONNECT——斷開連線
1.2 本節目標
- SUBSCRIBE——訂閱主題
- SUBACK——訂閱確認
- UNNSUBSCRIBE——取消訂閱
- UNSUBACK——取消訂閱確認
- PUBLISH——釋出訊息(Qos0,服務質量等級下一節再說吧)
2 MQTT控制報文格式(補充)
2.1 控制報文的型別
名字 | 值 | 報文流動方向 | 描述 |
---|---|---|---|
SUBSCRIBE | 8 | client -> server | 客戶端訂閱請求 |
SUBACK | 9 | server -> client | 訂閱請求報文確認 |
PUBLISH | 3 | 雙向 | 釋出訊息 |
UNSUBSCRIBE | 10 | client -> server | 取消訂閱請求 |
UNSUBACK | 11 | server -> client | 取消訂閱報文確認 |
2.2 識別符號
控制報文 | 固定報文標誌 | Bit 3 | Bit 2 | Bit 1 | Bit 0 |
---|---|---|---|---|---|
SUBSCRIBE | Reserved | 0 | 0 | 1 | 0 |
SUBACK | Reserved | 0 | 0 | 0 | 0 |
PUBLISH | Used in MQTT 3.1.1 | DUP | QoS | QoS | RETAIN |
UNSUBSCRIBE | Reserved | 0 | 0 | 1 | 0 |
UNSUBACK | Reserved | 0 | 0 | 0 | 0 |
- DUP = 控制報文的重複分發標誌
- Qos = PUBLISH報文的服務質量等級
- RETAIN = PUBLISH報文是否保留標誌
2.3 報文識別符號
- 顧名思義就是標識報文的唯一性
- SUBSCRIBE,UNSUBSCRIBE和PUBLISH(QoS大於0)控制報文必須包含一個非零的16位報文識別符號(Packet Identifier)。
- Client每次傳送一個新的報文時都必須分配一個未使用的報文識別符號。
- Client如果進行重發報文必須使用相同的識別符號。
- 當Client處理完這個報文對應的確認後,這個報文識別符號就釋放可重用。
- QoS 1的PUBLISH對應的是PUBACK
- QoS 2的PUBLISH對應的是PUBCOMP
- SUBSCRIBE對應的分別是SUBACK
- UNSUBSCRIBE對應的時UNSUBACK
控制報文 | 報文識別符號欄位 |
---|---|
SUBSCRIBE | 需要 |
SUBACK | 需要 |
PUBLISH | 需要(如果QoS > 0,Qos=0時不能帶) |
UNSUBSCRIBE | 需要 |
UNSUBACK | 需要 |
2.4 有效載荷
控制報文 | 有效載荷 |
---|---|
SUBSCRIBE | 需要 |
SUBACK | 需要 |
PUBLISH | 可選(允許發空負載) |
UNSUBSCRIBE | 需要 |
UNSUBACK | 不需要 |
3 MQTT控制報文示例
3.1 SUBSCRIBE——訂閱主題
(1)WireShark抓包獲取報文
MQ Telemetry Transport Protocol, Subscribe Request
# 固定報頭
Header Flags: 0x82 (Subscribe Request)
1000 .... = Message Type: Subscribe Request (8)
.... 0010 = Reserved: 2
Msg Len: 9
# 可變報頭
Message Identifier: 1 # 2個位元組,0x0001,報文識別符號
# 有效載荷
Topic Length: 4 # 2個位元組,0x0004,Topic的長度
Topic: TEST # 2個位元組,0x54455354,Topic的值
Requested QoS: At most once delivery (Fire and Forget) (0) # 1個位元組 Qos0 0x00
(2)Topic
如果服務端選擇不支援包含萬用字元的主題過濾器,必須拒絕任何包含萬用字元過濾器的訂閱請求。
Qos為服務質量要求,後面一節講。
(3)響應
- Server收到Client傳送的一個SUBSCRIBE報文時,必須使用SUBACK報文響應
- SUBACK報文必須和等待確認的SUBSCRIBE報文有相同的報文識別符號
- 允許Server在傳送SUBACK報文之前就開始傳送與訂閱匹配的PUBLISH報文
- 如果Server收到的SUBSCRIBE報文,Topic與現存的Topic相同,則使用新的訂閱(新Qos)替換現存的訂閱。
- Client被授權的Qos為0,那麼Client傳送Qos2將會降級為0。
3.2 SUBACK – 訂閱確認
(1)WireShark抓包獲取報文
MQ Telemetry Transport Protocol, Subscribe Ack
# 固定報頭
Header Flags: 0x90 (Subscribe Ack)
1001 .... = Message Type: Subscribe Ack (9)
.... 0000 = Reserved: 0
Msg Len: 3
# 可變報頭
Message Identifier: 1 # 2個位元組,0x0001,與SUBSCRIBE相同
# 有效載荷
Granted QoS: At most once delivery (Fire and Forget) (0) # 返回碼,最大Qos
(2)返回碼
- 0x00 - 成功,最大Qos0
- 0x01 - 成功,最大Qos1
- 0x02 - 成功,最大Qos2
- 0x80 - 失敗
3.3 PUBLISH – 釋出訊息(Qos0)
(1)WireShark抓包獲取報文
MQ Telemetry Transport Protocol, Publish Message
# 固定報頭
Header Flags: 0x30 (Publish Message)
0011 .... = Message Type: Publish Message (3) # 代表釋出訊息
.... 0... = DUP Flag: Not set # 重發標誌
.... .00. = QoS Level: At most once delivery (Fire and Forget) (0) # Qos
.... ...0 = Retain: Not set # 保留標誌
Msg Len: 16
# 可變報頭
### 主題名
Topic Length: 4 # 2個位元組,0x0004
Topic: TEST # 4個位元組
### 報文識別符號,當Qos不為0時,必須帶上。
# 有效載荷
Message: HelloWorld
(2)固定報頭
重發標誌:DUP為0,代表Client第一次發這個報文;DUP為1,代表Client重發已發的報文。對於Qos為0時,DUP必須為0。
服務質量等級
Qos | Bit2 | Bit1 | 描述 |
---|---|---|---|
0 | 0 | 0 | 最大分發一次 |
1 | 0 | 1 | 至少一次 |
2 | 1 | 0 | 只分發一次 |
- | 1 | 1 | 保留位 |
(3)保留標誌(RETAIN)
- Client傳送給Server報文的RETAIN為0,Server不能儲存這個訊息,也不能移除或替換任何現存的訊息。
- Client傳送給Server報文的RETAIN為1,Server必須儲存該訊息和Qos,以便分發給未來的主題名匹配的訂閱者。(如果有新的訂閱,Server存在最近保留的訊息,它將傳送保留訊息給新的Client。並且Server傳送的該訊息必須將RETAIN設為0.)
- Server不保留零位元組(有效載荷)的保留訊息。
- Client傳送給Server報文的RETAIN為1,Qos為0,Server必須丟棄之前為那個Topic保留的任何訊息,並保留該訊息,但是該訊息也是可以被丟棄的。
(4)響應
服務質量等級 | 預期響應 |
---|---|
Qos0 | 無響應 |
Qos1 | PUBACK報文 |
Qos2 | PUBREC報文 |
(5)動作
- 客戶端使用帶萬用字元的主題過濾器請求訂閱時,客戶端的訂閱可能會重複,因此釋出的訊息可能會匹配多個過濾器,所以服務端必須將訊息分發給所有訂閱匹配的QoS等級最高的客戶端。
3.4 UNSUBSCRIBE –取消訂閱
(1)WireShark抓包獲取報文
MQ Telemetry Transport Protocol, Unsubscribe Request
# 固定報頭
Header Flags: 0xa2 (Unsubscribe Request)
1010 .... = Message Type: Unsubscribe Request (10)
.... 0010 = Reserved: 2
Msg Len: 8
# 可變報頭
Message Identifier: 2 # 報文識別符號,0x0002
# 有效載荷
Topic Length: 4
Topic: TEST
(2)響應
- UNSUBSCRIBE報文提供的主題過濾器(無論是否包含萬用字元)必須與服務端持有的這個客戶端的當前主題過濾器集合逐個字元比較。如果有任何過濾器完全匹配,那麼它(Server)自己的訂閱將被刪除,否則直接返回SUBACK響應。
- Server刪除了一個訂閱:
- 必須停止分發任何新訊息給該Client
- 必須完成分發任何已經開始發往Client傳送的QoS 1和QoS 2的訊息
- 可以繼續傳送任何現存的準備分發給客戶端的快取訊息
- 即使Server沒有刪除任何訂閱訊息,伺服器也需要傳送一個SUBACK響應。
- 如果服務端收到包含多個主題過濾器的UNSUBSCRIBE報文,它必須如同收到了一系列的多個UNSUBSCRIBE報文一樣處理那個報文,除了將它們的響應合併到一個單獨的UNSUBACK報文外。(這句話有疑惑??)
3.5 UNSUBACK – 取消訂閱確認
(1)WireShark抓包獲取報文
MQ Telemetry Transport Protocol, Unsubscribe Ack
Header Flags: 0xb0 (Unsubscribe Ack)
1011 .... = Message Type: Unsubscribe Ack (11)
.... 0000 = Reserved: 0
Msg Len: 2
Message Identifier: 2
4 訂閱的主題名與主題過濾器
4.1 主題萬用字元
- 主題分割符號:/
- 主題多層萬用字元:#,必須在最後面且只有一個
- sport/tennis/#/ranking 無效
- sport/tennis#" 無效
- 主題單層萬用字元:+,可以有多個
- sport+ 無效
# demo1:多層萬用字元
訂閱:"sport/tennis/player1/#"
收到:sport/tennis/player1
sport/tennis/player1/ranking
sport/tennis/player1/score/wimbledon
# demo2: 單層萬用字元
訂閱:"sport/+"
收到: sport/tennis
收不到: sport
sport/
# demo3:單層萬用字元
訂閱:"+"
收到: sport
收不到: sport/
# demo4:單層萬用字元
訂閱:"+/"
收到: sport/
收不到: sport
4.2 以$開頭的主題(非絕對)
- $通常預留給伺服器使用(非絕對,具體看伺服器的配置)
- $SYS/ 被廣泛用作包含伺服器特定資訊或控制介面的主題的字首
- 訂閱”#“不會收到”$“開頭的主題訊息
- +/monitor/Clients不會收到的客戶端不會收到任何釋出到 “$SYS/monitor/Clients” 的訊息
4.3 主題語義和用法
- 只包含斜槓 “/” 的主題名或主題過濾器是合法的
- 主題名和主題過濾器可以包含空格
- 主題名和主題過濾器是區分大小寫的
- 主題名或主題過濾器以前置或後置斜槓 “/” 區分
- 主題名和主題過濾器不能包含空字元(Unicode U+0000)
- 主題名和主題過濾器不能只有空格符
- 主題名和主題過濾器是UTF-8編碼字串,它們不能超過65535位元組