1 准備階段
- MQTT客戶端:https://www.cnblogs.com/linzhanfly/p/9923577.html
- WireShark
- MQTT服務器(iot.eclipse.org)
- TCP:tcp://iot.eclipse.org:1883
- WebSocket:ws://iot.eclipse.org:80/ws
- MQTT協議(v3.1.1)下載地址:https://www.v2ex.com/t/209491
- 本文基於MQTT V3.1.1
2 MQTT控制報文格式
2.1 MQTT控制報文結構
- 固定報頭,所有控制報文都包含
- 可變報頭,部分控制報文包含
- 有效負載,部分控制報文包含
2.2 固定報頭
| Bit | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
| byte 1 | MQTT 控制報文的類型 | 用於指定控制報文類型的標志符 | ||||||
| byte 2 | 剩余長度(可變報頭長度 + 有效負載長度) | |||||||
- 僅顯示部分,具體請參考協議,后面具體用到再說
| 名字 | 值 | 報文流動方向 | 描述 |
|---|---|---|---|
| CONNECT | 1 | client -> server | client 請求連接 server |
| CONNACK | 2 | server -> client | 連接報文確認 |
| PINGREQ | 12 | client -> server | 心跳請求 |
| PINGRESP | 13 | server -> client | 心跳響應 |
| DISCONNECT | 14 | client -> server | 客戶端斷開連接 |
(2)標識符
| 控制報文 | 固定報文標志 | Bit 3 | Bit 2 | Bit 1 | Bit 0 |
|---|---|---|---|---|---|
| CONNECT | Reserved | 0 | 0 | 0 | 0 |
| CONNACK | Reserved | 0 | 0 | 0 | 0 |
| PINGREQ | Reserved | 0 | 0 | 0 | 0 |
| PINGRESP | Reserved | 0 | 0 | 0 | 0 |
| DISCONNECT | Reserved | 0 | 0 | 0 | 0 |
(3)剩余長度
- 表示當前報文剩余部分的字節數,包括可變報頭和負載的數據。
- 它采用了可變長度的編碼方法。
- 允許最大長度256M
| 字節數 | 最小值 | 最大值 |
|---|---|---|
| 1 | 0(0x00) | 127(0x7F) |
| 2 | 128(0x80,0x01) | 16383(0xFF,0x7F) |
| 3 | 16384(0x80,0x80,0x01) | 2097151(0xFF,0xFF,0x7F) |
| 4 | 2097152(0x80,0x80,0x80,0x01) | 268435455(0xFF,0xFF,0xFF,0x7F) |
2.3 可變報頭
| Bit | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
| byte 1 | 報文標識符 MSB | |||||||
| byte 2 | 報文標識符 LSB | |||||||
2.4 有效載荷
| 控制報文 | 有效載荷 |
|---|---|
| CONNECT | 需要 |
| CONNACK | 不需要 |
| PINGREQ | 不需要 |
| PINGRESP | 不需要 |
| DISCONNECT | 不需要 |
3 MQTT控制報文示例
3.1 CONNECT——連接服務器
(1)WireShark抓包獲取報文
MQ Telementry Transport Protocol, Connect Command
# 固定報頭(2~5個字節)
Header Flags: 0x10 (Connect Command) # 固定報頭,1個字節
0001 .... = Message Type: Connect Command(1) # 報文類型: CONNECT
.... 0000 = Reserved: 0 # 保留
Msg Len: 40 # 剩余長度,0x28,1個字節
# 可變報頭,本例10個字節
Protocol Name Length: 4 # 協議長度,0x0004,2個字節
Protocol Name: MQTT # 協議名,MQTT(ASCII:0x4d515454),4個字節
Version: MQTT v3.1.1 (4) # 協議版本,0x04 1個字節
Connect Flags: 0xC2 # 連接標志,1個字節,每一位代表不同的內容
1... .... = User Name Flag: Set # 用戶名
.1.. .... = Password Flag: Set # 密碼
..0. .... = Will Retain: Not Set # 遺囑保留
...0 0... = Qos Level: At most once deliver (Fire and Forget) (0) # Qos
.... .0.. = Will Flag: Not Set # 遺囑
.... ..1. = Clean Session Flag: Set # 清除會話
.... ...0 = (Reserverd): Not Set # 固定為0
Keep Alive: 60 # 保持連接,2個字節,0x003C
# 有效載荷,本例30個字節
Client ID Length: 14,# 2個字節,0x000e
Client ID: MQTT_FX_client # 14個字節
User Name Length: 5 # 2個字節,0x0005
User Name: hello # 5個字節
Password Length: 5 # 2個字節,0x0005
Password: world # 5個字節
10 28 00 04 4d 51 54 54 04 c2 00 3c 00 0e 4d 51 .(..MQTT...<..MQ
54 54 5f 46 58 5f 43 6c 69 65 6e 74 00 05 68 65 TT_FX_Client..he
6c 6c 6f 00 05 77 6f 72 6c 64 llo..world
(2)連接標志
-
保留(第0位):固定為0
-
清理會話(第1位)設置為1,代表服務器不需要保存會話狀態,即丟棄會話並開始一個新的會話;如果為0,需要根據保存的會話狀態恢復當前會話。
- 客戶端會話狀態:
- 發送給Sever,但沒有完成確認的Qos1和Qos2的消息
- 已經從接收,但沒有完成確認的Qos2的消息
- 服務器會話狀態:
- 會話是否存在
- 客戶端的訂閱消息
- 發送給Client,但沒有完成確認的Qos1和Qos2消息
- 即將傳輸給Client的Qos1和Qos2消息
- 從Client接收,但沒有完成確認的Qos2消息
- 可選,准備發送給Client的Qos0消息
- 客戶端會話狀態:
-
遺囑標志(第2位):設置為1時,CONNECT報文的有效在載荷中包含Will Topic和Will Message字段,如果連接請求成功,遺囑(Will Message)消息必須被存儲在服務端。之后網絡連接關閉時,服務端必須發布這個遺囑消息,除非服務端收到DISCONNECT報文時刪除了該遺囑消息。下面情況將發生發布遺囑消息:
- Server檢測到一個I/O錯誤或者網絡故障
- Client在Keep Alive時間內未能通信(既沒有發送PING)
- Client沒有先DISCONNECT報文就直接關閉了網絡連接
- 協議錯誤Server關閉了網絡連接
-
遺囑Qos(第3 4位):用於指定發布遺囑消息時的服務質量等級
- 遺囑標志為0,遺囑Qos也必須為0。
- 遺囑標志為1,遺囑Qos可以為任一服務質量等級。
-
遺囑保留(第5位):遺囑消息發布時是否需要保留。
-
用戶名與密碼標志(第6 7位)如果為1,代表有效載荷中帶有用戶名和密碼,服務器可以獲取並進行驗證
(3)保持連接時間
-
2個字節表示,以秒為單位,最大值為18小時12分15秒
-
斷開情況:
- Client發送PINGREQ,在合理時間內沒有回來則關閉到Server的網絡連接
- Server在1.5 * 保持連接時間 內沒有收到Client的任意控制報文,它將斷開Client的網絡連接
-
設置為0,代表Server需要因為Client的不活躍而斷開連接,但是Server仍然可以在需要的時候關閉連接。
(4)有效載荷
- 客戶端標識符(Must):長度 + 標識符(大小寫英文或數字),有效載荷的第一個字段,客戶端可以提供一個零字節的ClientId,但是Clean Seesion必需為1.
- 遺囑主體(Option):Will Topic
- 遺囑消息(Option):Will Message
- 用戶名(Option):Username
- 密碼(Option)Password
(5)注意
MQTT Server允許Client發送完CONNECT就發送訂閱或者發布消息,不過如果驗證不通過,Server將關閉連接拒收后面的消息。
3.2 CONNACK——確認連接請求
(1)WireShark抓包獲取報文
MQ Telementry Transport Protocol, Connect Command
# 固定報頭(2~5個字節)
Header Flags: 0x20 (Connect Ack) # 固定報頭,1個字節
0010 .... = Message Type: Connect Ack (2) # 報文類型: CONNACK
.... 0000 = Reserved: 0 # 保留
Msg Len: 2 # 剩余長度,0x02,1個字節
# 可變報頭
Acknowledge Flags: 0x00 # 連接確認標志
0000 000. = Reserved: Not Set # 固定為0
.... ...0 = Session Present: Not Set # 當前會話標志
Return Code: Connection Accepted (0) # 連接返回碼
(2)當前會話標志
- 如果Server收到一個CleanSession為1的連接,CONNACK報文中的返回碼設置為0之外,還需要將CONNACK報文中的當前會話標志設置為0
- 如果Server收到一個CleanSeesion為0的連接,當前會話標志的值取決於Server是否保存了ClientId對應客戶端的會話狀態。如果保存了,當前會話標志設置為1,否則為0。
(3)連接返回碼
| 值 | 返回碼響應 | 描述 |
|---|---|---|
| 0 | 0x00 | 連接已接收 |
| 1 | 0x01 | 不支持的協議版本 |
| 2 | 0x02 | 不合格的客戶端標識符(UTF-8編碼) |
| 3 | 0x03 | MQTT服務端不可用(TCP已連接) |
| 4 | 0x04 | 無效的用戶名或密碼 |
| 5 | 0x05 | 未授權此客戶端 |
| 6-255 | 保留 |
3.3 PINGREQ——心跳請求
(1)WireShark抓包獲取報文
Header Flags: 0xC0 (Ping Request) # 固定報頭
1100 .... = Message Type: Ping Request (12)
.... 0000 = Reserved: 0
Msg Len: 0
(2)作用
- 在沒有任何其他控制報文發送給Server時,Client告知Server還活着
- 請求Server發送響應確認Server是否活着
- 使用網絡以確定網絡連接沒有斷開
3.4 PINGRESP——心跳響應
(1)WireShark抓包獲取報文
Header Flags: 0xD0 (Ping Response) # 固定報頭
1101 .... = Message Type: Ping Response(13)
.... 0000 = Reserved: 0
Msg Len: 0
(2)作用
- 告知Client,Server活着
3.5 DISCONNECT——斷開連接
(1)WireShark抓包獲取報文
Header Flags: 0xe0 (Disconnect) # 固定報頭
1110 .... = Message Type: Disconnect (14)
.... 0000 = Reserved: 0
Msg Len: 0
(2)作用
- 客戶端正常斷開連接
(3)響應
- Client發送DISCONNECT報文后
- 必需關閉網絡連接
- 不能通過該連接繼續發送任何控制報文
- Server收到DISCONNECT報文后
- 必須丟棄任何與當前連接關聯的未發布的遺囑消息
- 應該關閉網絡連接,如果客戶端還沒有這么做
