在上一篇文章,直接在本地搭建了服務(wù)器和客戶端,簡單的實踐了MQTT的用法。而這一篇來解析MQTT的報文格式。MQTT的報文字段很精簡。但是解析起來還是有些復(fù)雜的。
解析報文最好的工具是采用wireshark抓包,不過我發(fā)現(xiàn),wireshark的2.xxx的版本無法進行回環(huán)抓包(即無法抓取127.0.0.1的數(shù)據(jù)報文)。通過一番度娘,發(fā)現(xiàn)新版本的wireshark用Npcap替換WinPcap,Npcap是基于WinPcap 4.1.3開發(fā)的,api兼容WinPcap。
030
MQTT報文
MQTT報文格式
MQTT的報文字段主要包含3部分,如下表:
名稱 | 說明 |
---|---|
Fixed header(固定報文頭) | 所有MQTT報文都包含 |
Variable header(可變報文頭) | 只有部分MQTT報文包含 |
Payload(MQTT數(shù)據(jù)段) | 只有部分MQTT報文包含 |
MQTT固定報文頭[Fixed header]
每個MQTT報文都包含一個固定報文頭,固定報文頭部格式如下:
bit76543210 +-----+-----+-----+-----+-----+-----+-----+-----+-----+ |Byte1|MQTT控制報文類型|指定控制報文類型的標志| +-----+-----------------------------------------------| |Byte2|剩余長度| +-----+-----------------------------------------------| |...|剩余長度| +-----------------------------------------------------|
MQTT控制報文類型
MQTT的控制報文類型在固定報文頭的第1個字節(jié)的4 ~ 7bit,共4位無符號值。這些值如下表描述:
類型 | 值 | 報文方向 | 描述 |
---|---|---|---|
RESERVED | 0 | 禁止 | 保留 |
CONNECT | 1 | 客戶端到服務(wù)端 | 客戶端請求連接服務(wù)器 |
CONNACK | 2 | 服務(wù)端到客戶端 | 連接報文確認 |
PUBLISH | 3 | 雙向 | 發(fā)布消息 |
PUBACK | 4 | 雙向 | QoS 1消息發(fā)布收到確認 |
PUBREC | 5 | 雙向 | 發(fā)布收到(保證交付第一步) |
PUBREL | 6 | 雙向 | 發(fā)布釋放(保證交付第二步) |
PUBCOMP | 7 | 雙向 | QoS 2消息發(fā)布完成 |
SUBSCRIBE | 8 | 客戶端到服務(wù)端 | 客戶端訂閱請求 |
SUBACK | 9 | 服務(wù)端到客戶端 | 訂閱請求報文確認 |
UNSUBSCRIBE | 10 | 客戶端到服務(wù)端 | 客戶端取消訂閱請求 |
UNSUBACK | 11 | 服務(wù)端到客戶端 | 取消訂閱請求報文確認 |
PINGREQ | 12 | 客戶端到服務(wù)端 | 心跳請求 |
PINGRESP | 13 | 服務(wù)端到客戶端 | 心跳響應(yīng) |
DISCONNECT | 14 | 客戶端到服務(wù)端 | 客戶端斷開連接 |
RESERVED | 15 | 禁止 | 保留 |
MQTT控制報文標志
MQTT的控制報文標志在固定報文頭的第1個字節(jié)的4 ~ 7bit,包含每個MQTT報文類型的特定的標志。
注意:如果接收方收到非法的標志,接受者必須關(guān)閉網(wǎng)絡(luò)連接。標志如下表:
其中:
DUP:控制報文的重復(fù)分發(fā)標志
QoS:PUBLISH報文的服務(wù)質(zhì)量等級
RETAIN:PUBLISH報文的保留標志
類型 | 報文標志 | Bit3 | Bit2 | Bit1 | Bit0 |
---|---|---|---|---|---|
CONNECT | Reserved | 0 | 0 | 0 | 0 |
CONNACK | Reserved | 0 | 0 | 0 | 0 |
PUBLISH | Used in MQTT 3.1.1 | DUP | QoS | QoS | RETAIN |
PUBACK | Reserved | 0 | 0 | 0 | 0 |
PUBREC | Reserved | 0 | 0 | 0 | 0 |
PUBREL | Reserved | 0 | 0 | 1 | 0 |
PUBCOMP | Reserved | 0 | 0 | 0 | 0 |
SUBSCRIBE | Reserved | 0 | 0 | 1 | 0 |
SUBACK | Reserved | 0 | 0 | 0 | 0 |
UNSUBSCRIBE | Reserved | 0 | 0 | 1 | 0 |
UNSUBACK | Reserved | 0 | 0 | 0 | 0 |
PINGREQ | Reserved | 0 | 0 | 0 | 0 |
PINGRESP | Reserved | 0 | 0 | 0 | 0 |
DISCONNECT | Reserved | 0 | 0 | 0 | 0 |
MQTT報文剩余長度
剩余長度字段從固定報文頭的第2個字節(jié)開始,最長可達4個字節(jié),所以剩余長度訪問是Byte[2 ~ 5]。
剩余長度表示當前報文剩余部分的字節(jié)數(shù),包含可變頭部和Payload。
上面的描述,那么怎么確定其長度用幾個字節(jié)來描述呢?答案:取決于字節(jié)的最高位Bit7(默認都是在搞自己在前);如果Bit7為1,那么需要繼續(xù)計算字節(jié)長度,如果Bit7為0,那么不需要繼續(xù)計算字節(jié)長度。
消息長度可以簡單理解為128禁止的數(shù)據(jù),4位長度最大可以表示:128 * 128 * 128 * 128 Byte = 256MB。
需注意計算規(guī)則,低位在前,高位在后,字節(jié)最高位Bit7標記是否繼續(xù)計算消息長度。其消息長度范圍如下表:
字節(jié) | 最小值 | 最大值 |
---|---|---|
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) |
舉例:
第一字節(jié)最高位是1,需要繼續(xù)向后計算,去掉標記位(0xC1%128),得到1000001=41
第二字節(jié)最高位是1,需要繼續(xù)向后計算,去掉標記位(0xC2%128),得到1000010=42
第三字節(jié)最高位是0,不需要向后計算,其結(jié)果就是0x33=51
因為低位在前,高位在后,即消息長度為:41 + 42 * 128 + 51 * 128 * 128=841001Byte = 821KB
0xC1 = 11000001
0xC2 = 11000010
0x33 = 00110011
消息長度是0x60,其二進制是01100000b,字節(jié)最高位Bit7位0,所以不需要往后計算,其十進制是96(即消息長度為96個字節(jié))。
消息長度是0xC1, 0xC2, 0x33。分別二進制為:
MQTT可變報文頭[Variable header]
在某些MQTT控制報文包含了一個可變報文頭部分,它在固定報文頭和payload之間,可變報頭的內(nèi)容根據(jù)報文類型的不同而不同,可變報頭的報文標識符(Packet Identifier)字段存在與多個類型的報文里。可變報頭其實就是MQTT開發(fā)中使用的Packet ID,通過Packet ID 進行一些操作確認。包含Packet ID的報文類型如下:
類型 | 包含可變報文頭 |
---|---|
PUBLISH | √(QoS > 0) |
PUBACK | √ |
PUBREC | √ |
PUBREL | √ |
PUBCOMP | √ |
SUBSCRIBE | √ |
SUBACK | √ |
UNSUBSCRIBE | √ |
UNSUBACK | √ |
Packet ID默認是從1開始并自增,如果一個Packet ID被用完后,這個Packet ID可以被重用。對于PUBLISH(QoS 1)來說,如果發(fā)送端接收到PUBACK,那么這個Packet ID就用完了。對于PUBLISH(QoS 2),如果接收方收到PUBCOMP,那么這個Packet ID就用完了。對于SUBSCRIBE和UNSUBSCRIBE,Packet ID使用完成的標記是發(fā)送方收到了對應(yīng)的SUBACK和UNSUBACK。
MQTT數(shù)據(jù)段[Payload]
MQTT中有些報文類型是包含Payload
如PUBLISH的Payload指消息內(nèi)容。
如CONNECT的Payload指Client Identifier,Will Topic,Will Message,Username,Password等信息
包含Payload的報文類型如下:
類型 | 包含Payload |
---|---|
CONNECT | √ |
PUBLISH | 可選 |
SUBSCRIBE | √ |
SUBACK | √ |
UNSUBSCRIBE | √ |
通過wireshark分析MQTT報文
因為我們的服務(wù)器和客戶端是在PC上搭建的,所以需要通過wireshark的回環(huán)抓包,來分析報文類型CONNECT和CONNACK。
打開wireshark,選擇進行回環(huán)抓包
使用MQTT.fx創(chuàng)建一個客戶端,點擊連接,便可以抓到CONNECT和CONNACK的報文。
紅色圈子的報文類型CONNECT的內(nèi)容:
內(nèi)容: 1025 00044d515454 04 c2 003c 0008636c69656e743031 000561646d696e 00083132333435363738
報文類型CONNECT內(nèi)容分析:
0x10:高四位0001,代表報文類型:CONNECT。
0x25:二進制-0010 0101,Bit7為0,所以剩余長度只有一個字節(jié)長,即0x25十進制:37個字節(jié)
0x00 0x04 0x4d 0x51 0x54 0x54:其中-0x00,0x04表示協(xié)議長度;0x4d 0x51 0x54 0x54對應(yīng) "M", "Q", "T", "T"。
0x04:代表MQTT版本號:v3.1.1
0xc2: 次字節(jié)含義如下表,該字節(jié)描述緊跟數(shù)據(jù)有 User Name、Password,沒有遺囑設(shè)置,選擇了清理會話方式與服務(wù)器連接。
Bit | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
---|---|---|---|---|---|---|---|---|
含義 | User Name Flag | Password Flag | Will Retain Flag | Will QoS MSB | Will QoS LSB | Will Flag | Clean session | Rreserved |
值 | 1 | 1 | 0 | 0 | 0 | 0 | 1 | 0 |
00 3c:對應(yīng)十進制為60,即保持連接(Keep Alive)60秒,以秒為單位,它是指在客戶端傳輸完成一個控制報文的時刻到發(fā)送下一個報文的時刻,兩者之間允許空閑的最大時間間隔。客戶端負責保證控制報文發(fā)送的時間間隔不超過保持連接的值。如果沒有任何其它的控制報文可以發(fā)送,客戶端 必須發(fā)送一個PINGREQ 報文。客戶端隨時可以發(fā)送ping指令,服務(wù)器如果發(fā)現(xiàn)在KeepAalive時間內(nèi)沒有收到客戶端的消息,會自動斷開與客戶端建立的連接。
00 08 63 6c 69 65 6e 74 30 31:其中-0x00, 0x08表示clientID的長度8個字節(jié);0x63,0x6c,0x69,0x65,0x6e,0x74,0x30,0x31:代表client01。即是我們在MQTT.fx創(chuàng)建客戶端的時候設(shè)置clientID。
00 05 61 64 6d 69 6e:其中-0x00,0x05表示User Name的的長度5個字節(jié);0x61,0x64,0x6d,0x69,0x6e:代表User Name為admin,即是我們在MQTT.fx創(chuàng)建客戶端的時候設(shè)置User Name。
00 08 31 32 33 34 35 36 37 38:其中-0x00,0x08表示User Name的的長度8個字節(jié);0x31,0x32,0x33,0x34,0x35,0x36,0x37,0x38:代表Password為12345678,即是我們在MQTT.fx創(chuàng)建客戶端的時候設(shè)置Password。
紅色圈子的報文類型CONNACK的內(nèi)容:
內(nèi)容: 20 02 00 00
報文類型CONACK內(nèi)容分析:
20:高四位0010,代表報文類型:CONNACK。
02:二進制-0000 0010,Bit為0,所以剩余長度只有一個字節(jié)長,即0x02十進制:2個字節(jié)。
00:可變頭部的第一個字節(jié)的第0位連接確認。
00:可變頭部的第二個字節(jié)。
值 | 返回碼響應(yīng) | 描述 |
---|---|---|
0 | 0x00連接已接受 | 連接已被服務(wù)器接受 |
1 | 0x01連接已拒絕,不支持的協(xié)議版本 | 服務(wù)器不支持客戶端請求的協(xié)議版本 |
2 | 0x02連接已拒絕,不合格的客戶端ID | 客戶端ID是正確的UTF-8碼,但服務(wù)器不允許使用 |
3 | 0x03連接已拒絕,服務(wù)端不可用 | 網(wǎng)絡(luò)連接已建立,但MQTT服務(wù)不可用 |
4 | 0x04連接已拒絕,無效的用戶名或密碼 | 用戶名或密碼的數(shù)據(jù)格式無效 |
5 | 0x05連接已拒絕,未授權(quán) | 客戶端未被授權(quán)連接到此服務(wù)器 |
6-255 | Reserved | 保留 |
原文標題:教你動手寫網(wǎng)絡(luò)協(xié)議棧-MQTT報文解析6-解析
文章出處:【微信公眾號:RTThread物聯(lián)網(wǎng)操作系統(tǒng)】歡迎添加關(guān)注!文章轉(zhuǎn)載請注明出處。
-
網(wǎng)絡(luò)
+關(guān)注
關(guān)注
14文章
7713瀏覽量
90170 -
報文數(shù)據(jù)
+關(guān)注
關(guān)注
0文章
3瀏覽量
7683
原文標題:教你動手寫網(wǎng)絡(luò)協(xié)議棧-MQTT報文解析6-解析
文章出處:【微信號:RTThread,微信公眾號:RTThread物聯(lián)網(wǎng)操作系統(tǒng)】歡迎添加關(guān)注!文章轉(zhuǎn)載請注明出處。
發(fā)布評論請先 登錄
相關(guān)推薦
深入淺出解析低功耗藍牙協(xié)議棧

工業(yè)智能網(wǎng)關(guān)實現(xiàn)北向自定義報文配置流程

PROFINET通訊協(xié)議報文解析
基于MQTT協(xié)議的車云通信設(shè)計

百問MQTT協(xié)議分析 - 報文分析①
百問MQTT協(xié)議分析 - MQTT簡述及協(xié)議報文格式組成
MQTT協(xié)議網(wǎng)關(guān)的工作原理及功能特性

Linux網(wǎng)絡(luò)協(xié)議棧的實現(xiàn)

串口服務(wù)器和TCP/IP協(xié)議棧是什么關(guān)系
基于MQTT協(xié)議云平臺的Modbus轉(zhuǎn)MQTT網(wǎng)關(guān)

自動售貨機MDB協(xié)議中文解析(四) 通信格式
如何通過北向MQTT自定義報文格式對接到物聯(lián)網(wǎng)云平臺

評論