91欧美超碰AV自拍|国产成年人性爱视频免费看|亚洲 日韩 欧美一厂二区入|人人看人人爽人人操aV|丝袜美腿视频一区二区在线看|人人操人人爽人人爱|婷婷五月天超碰|97色色欧美亚州A√|另类A√无码精品一级av|欧美特级日韩特级

0
  • 聊天消息
  • 系統(tǒng)消息
  • 評論與回復(fù)
登錄后你可以
  • 下載海量資料
  • 學(xué)習(xí)在線課程
  • 觀看技術(shù)視頻
  • 寫文章/發(fā)帖/加入社區(qū)
會員中心
創(chuàng)作中心

完善資料讓更多小伙伴認(rèn)識你,還能領(lǐng)取20積分哦,立即完善>

3天內(nèi)不再提示

Linux應(yīng)用開發(fā)【第十六章】MQTT協(xié)議分析應(yīng)用開發(fā)

weidongshan ? 來源:weidongshan ? 作者:weidongshan ? 2021-12-10 19:32 ? 次閱讀
加入交流群
微信小助手二維碼

掃碼添加小助手

加入工程師交流群

文章目錄

16 MQTT協(xié)議分析應(yīng)用開發(fā)

16.1 mqtt協(xié)議介紹

16.1.1 概述

16.1.2 特點

16.1.3 應(yīng)用

16.2 mqtt協(xié)議報文格式組成

16.2.1 mqtt控制報文結(jié)構(gòu)

16.2.2 mqtt固定報頭

16.2.3 mqtt控制報文類型

16.2.4 標(biāo)記

16.2.5 剩余長度

16.2.5.1 示例

16.2.6 可變報頭

16.2.7 有效載荷

16.3 報文分析

16.3.1 CONNECT-連接服務(wù)端

16.3.1.1 connect固定報頭

16.3.1.2 協(xié)議名字節(jié)組成

16.3.1.3 協(xié)議級別

16.3.1.4 連接標(biāo)記

16.3.1.5 保持連接

16.3.1.6 客戶端標(biāo)識符

16.3.1.7 遺囑主題

16.3.1.8 遺囑消息

16.3.1.9 用戶名和密碼

16.3.10.1 wirshark抓包分析connect報文

16.3.10.2 c語言構(gòu)造mqtt connect報文

16.3.2 CONNACK-確認(rèn)連接請求

16.3.2.1 固定報頭

16.3.2.2 可變報頭

16.3.2.3 CONNACK報文wireshark抓包分析

16.3.2.4 c語言構(gòu)造connect ack報文

16.3.3 PUBLISH-發(fā)布消息

16.3.3.1 固定報頭

16.3.3.2 抓包分析PUBLISH報文

16.3.3.3 構(gòu)造publish 報文

16.3.4 PUBREC-發(fā)布收到

16.3.4.1 固定報頭

16.3.4.2 PUBREC抓包報文

16.3.4.3 c語言構(gòu)造pubrec報文

16.3.5 PUBREL-發(fā)布釋放

16.3.5.1 固定報頭

16.3.5.2 PUBREL抓包報文

16.3.5.3 c語言構(gòu)造pubrel報文

16.3.6 PUBCOMP-發(fā)布完成

16.3.6.1 固定報頭

16.3.6.2 PUBCOMP抓包報文

16.3.6.3 c語言構(gòu)造pubcom報文

16.3.7 PINGREQ-心跳請求

16.3.7.1 固定報頭

16.3.7.2 PINGREQ 抓包報文

16.3.7.3 c語言構(gòu)造pingreq報文

16.3.8 PINGRESP – 心跳響應(yīng)

16.3.8.1 固定報頭

16.3.8.2 PINGRESP 抓包報文

16.3.8.3 c語言構(gòu)造pinpresp報文

16.3.9 DISCONNECT –斷開連接

16.3.9.1 固定報頭

16.3.9.2 DISCONNECT 抓包報文

16.3.9.3 c語言構(gòu)造disconnect報文

16.3.10 SUBSCRIBE-訂閱主題

16.3.10.1 固定報頭

16.3.10.2 SUBSCRIBE報文抓包

16.3.10.3 c語言構(gòu)造subscribe報文

16.3.11 SUBACK –訂閱確認(rèn)

16.3.11.1 固定報頭

3.11.2 SUBACK抓包報文

16.3.11.3 c語言構(gòu)造suback報文

16.3.12 UNSUBSCRIBE –取消訂閱

16.3.12.1 固定報頭

16.3.12.2 UNSUBSCRIBE抓包報文

16.3.12.3 c語言構(gòu)造unsubscribe報文

16.3.13 UNSUBACK –取消訂閱確認(rèn)

16.3.13.1 固定報頭

16.3.12.2 UNSUBSCRIBE ACK抓包報文

16.3.12.3 c語言構(gòu)造unsubscribe報文

16.3.14 服務(wù)端與客戶端交互操作過程

16.3.14.1 編譯

16.3.14.2 執(zhí)行

16 MQTT協(xié)議分析應(yīng)用開發(fā)

16.1 mqtt協(xié)議介紹

16.1.1 概述

MQTT是一個客戶端服務(wù)端架構(gòu)的發(fā)布/訂閱模式的消息傳輸協(xié)議。它的設(shè)計思想是輕巧、開放、簡單、規(guī)范,易于實現(xiàn)。這些特點使得它對很多場景來說都是很好的選擇,特別是對于受限的環(huán)境如機(jī)器與機(jī)器的通信M2M)以及物聯(lián)網(wǎng)環(huán)境(IoT)。

16.1.2 特點

a) 開放消息協(xié)議,簡單易實現(xiàn)

b) 發(fā)布訂閱模式,一對多消息發(fā)布

c) 基于TCP/IP網(wǎng)絡(luò)連接

d) 1字節(jié)固定報頭,2字節(jié)心跳報文,報文結(jié)構(gòu)緊湊

e) 消息QoS支持,可靠傳輸保證

16.1.3 應(yīng)用

MQTT協(xié)議廣泛應(yīng)用于物聯(lián)網(wǎng)、移動互聯(lián)網(wǎng)、智能硬件、車聯(lián)網(wǎng)、電力能源等領(lǐng)域。

a) 物聯(lián)網(wǎng)M2M通信,物聯(lián)網(wǎng)大數(shù)據(jù)采集

b) Android消息推送,WEB消息推送

c) 移動即時消息,例如Facebook Messenger

d) 智能硬件、智能家具、智能電器

e) 車聯(lián)網(wǎng)通信,電動車站樁采集

f) 智慧城市、遠(yuǎn)程醫(yī)療、遠(yuǎn)程教育

g) 電力、石油與能源等行業(yè)市場

16.2 mqtt協(xié)議報文格式組成

16.2.1 mqtt控制報文結(jié)構(gòu)

MQTT 協(xié)議通過交換預(yù)定義的 MQTT 控制報文來通信。 這一節(jié)描述這些報文的格式。MQTT 控制報文由三部分組成,如下圖:

pYYBAGGzOreAP0HeAABZHrWeVHA190.png

圖2.1 mqtt報文組成

16.2.2 mqtt固定報頭

每個 MQTT 控制報文都包含一個固定報頭, 固定報頭的格式如下圖:

poYBAGGzOreALMsLAABLE738lek049.png

圖2.2 mqtt固定報頭

16.2.3 mqtt控制報文類型

位置: 第 1 個字節(jié), 二進(jìn)制位 7-4,表示為 4 位無符號值。

MQTT 控制報文的類型:如下表:

Reserved 0 禁止 保留
CONNECT 1 客戶端到服務(wù)端 客戶端請求連接服務(wù)端
CONNACK 2 服務(wù)端到客戶端 連接報文確認(rèn)
PUBLISH 3 兩個方向都允許 發(fā)布消息
PUBACK 4 兩個方向都允許 QoS 1消息發(fā)布收到確認(rèn)
PUBREC 5 兩個方向都允許 發(fā)布收到(保證交付第一步)
PUBREL 6 兩個方向都允許 發(fā)布釋放(保證交付第二步)
PUBCOMP 7 兩個方向都允許 QoS 2消息發(fā)布完成(保證交互第三步)
SUBSCRIBE 8 客戶端到服務(wù)端 客戶端訂閱請求
SUBACK 9 服務(wù)端到客戶端 訂閱請求報文確認(rèn)
UNSUBSCRIBE 10 客戶端到服務(wù)端 客戶端取消訂閱請求
UNSUBACK 11 服務(wù)端到客戶端 取消訂閱報文確認(rèn)
PINGREQ 12 客戶端到服務(wù)端 心跳請求
PINGRESP 13 服務(wù)端到客戶端 心跳響應(yīng)
DISCONNECT 14 客戶端到服務(wù)端 客戶端斷開連接
Reserved 15 禁止 保留
名字 報文流動方向 描述

16.2.4 標(biāo)記

固定報頭第 1 個字節(jié)的剩余的 4 位 [3-0]包含每個 MQTT 控制報文類型特定的標(biāo)志 。標(biāo)記位說明如下表所示:

CONNECT Reserved 0 0 0 0
CONNACK Reserved 0 0 0 0
PUBLISH Used in MQTT 3.1.1 DUP1 QoS2 QoS2 RETAIN3
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
控制報文 固定報頭標(biāo)志 Bit 3 Bit 2 Bit 1 Bit 0

DUP1 =控制報文的重復(fù)分發(fā)標(biāo)志
QoS2 = PUBLISH 報文的服務(wù)質(zhì)量等級
RETAIN3 = PUBLISH 報文的保留標(biāo)志

16.2.5 剩余長度

位置:從第二個字節(jié)開始。剩余長度( Remaining Length) 表示當(dāng)前報文剩余部分的字節(jié)數(shù), 包括可變報頭和負(fù)載的數(shù)據(jù)。 剩余長度不包括用于編碼剩余長度字段本身的字節(jié)數(shù)。

pYYBAGGzOreALHPfAABbPCs-lGo988.png

圖2.3 剩余長度包含的報文范圍

剩余長度字段使用一個變長度編碼方案, 對小于 128 的值它使用單字節(jié)編碼。 更大的值按下面的方式處理。低 7 位有效位用于編碼數(shù)據(jù),最高有效位用于指示是否有更多的字節(jié)。 因此每個字節(jié)可以編碼 128 個數(shù)值和一個延續(xù)位( continuation bit) 。 剩余長度字段最大 4 個字節(jié)。

例如, 十進(jìn)制數(shù) 64 會被編碼為一個字節(jié), 數(shù)值是 64, 十六進(jìn)制表示為 0x40,。十進(jìn)制數(shù)字321(=65+2*128)被編碼為兩個字節(jié), 最低有效位在前。 第一個字節(jié)是 65+128=193。 注意最高位為
1 表示后面至少還有一個字節(jié)。 第二個字節(jié)是 2。

16.2.5.1 示例

123456 = 964 x 128 + 64 964 = 7x128 + 68 7 < 128 也就是123456 = (7 x 128 + 68)x128 + 64 展開:64 + 68 x128 + 7x128x128 第一字節(jié):64 | 0x80 = x (0x80=0x1000 0000或上最高位表示是否還有更多的字節(jié)) 第二字節(jié):68 | 0x80 = y (0x80=0x1000 0000或上最高位表示是否還有更多的字節(jié)) 第三字節(jié):7=z c語言表示:unsigned char len_byte[4] = { 64 | 128 , 68 | 128, 7 , 0 } 反過來,如果要算出123456 x-128 + (y-128)*128 + z x 128 x 128

把剩余長度轉(zhuǎn)換成字節(jié)表示:

poYBAGGzOriAT-5FAAB8t0UjIO8044.png

把字節(jié)轉(zhuǎn)換成剩余長度表示:

pYYBAGGzOriAXXQLAADfFqQ5dco937.png

16.2.6 可變報頭

某些 MQTT 控制報文包含一個可變報頭部分。 它在固定報頭和負(fù)載之間??勺儓箢^的內(nèi)容根據(jù)報文類型的不同而不同。報文標(biāo)識符是可變報頭一種,可變報頭的報文標(biāo)識符( Packet Identifier) 字段存在于在多個類型的報文里。

報文標(biāo)識符類型如下圖:

poYBAGGzOriAUoMcAAAvI1lyyC8134.png

圖2.4 報文標(biāo)識符

很多控制報文的可變報頭部分包含一個兩字節(jié)的報文標(biāo)識符字段。 這些報文是 PUBLISH( QoS>0 時) ,PUBACK, PUBREC, PUBREL, PUBCOMP, SUBSCRIBE, SUBACK, UNSUBSCIBE,UNSUBACK,如下表所示:

CONNECT 不需要
CONNACK 不需要
PUBLISH 需要(如果QoS > 0)
PUBACK 需要
PUBREC 需要
PUBREL 需要
PUBCOMP 需要
SUBSCRIBE 需要
SUBACK 需要
UNSUBSCRIBE 需要
UNSUBACK 需要
PINGREQ 不需要
PINGRESP 不需要
DISCONNECT 不需要
控制報文 報文標(biāo)識符字段

客戶端和服務(wù)端彼此獨(dú)立地分配報文標(biāo)識符。 因此,客戶端服務(wù)端組合使用相同的報文標(biāo)識符可以實現(xiàn)并發(fā)的消息交換。

例如,當(dāng)client發(fā)送一個packet Identifier =0x1234的報文給server時,server的回復(fù)報文packet identifier 必須是0x1234,Packet identifier 從1開始遞增,到達(dá)65535時,又從1開始計算。

poYBAGGzOrmATGuUAAAbUxUPFwg557.png

圖2.5 需要 Packet Identifier 的報文類型交互示意圖

16.2.7 有效載荷

某些 MQTT 控制報文在報文的最后部分包含一個有效載荷,帶有有效載荷報文類型如下表所示:

CONNECT 需要
CONNACK 不需要
PUBLISH 可選
PUBACK 不需要
PUBREC 不需要
PUBREL 不需要
PUBCOMP 不需要
SUBSCRIBE 需要
SUBACK 需要
UNSUBSCRIBE 需要
UNSUBACK 不需要
PINGREQ 不需要
PINGRESP 不需要
DISCONNECT 不需要
控制報文 有效載荷

16.3 報文分析

16.3.1 CONNECT-連接服務(wù)端

客戶端到服務(wù)端的網(wǎng)絡(luò)連接建立(完成三次握手)后,客戶端發(fā)送給服務(wù)端的第一個報文必須是 CONNECT 報文。

pYYBAGGzOrqAOrw9AABm3d75Hww292.png

圖3.1 三次握手與mqtt connect交互過程

在一個網(wǎng)絡(luò)連接上,客戶端只能發(fā)送一次 CONNECT 報文。服務(wù)端必須將客戶端發(fā)送的第二個 CONNECT報文當(dāng)作協(xié)議違規(guī)處理并斷開客戶端的連接。

有效載荷包含一個或多個編碼的字段。 包括客戶端的唯一標(biāo)識符, Will 主題, Will 消息, 用戶名和密碼。 除了客戶端標(biāo)識之外, 其它的字段都是可選的, 基于標(biāo)志位來決定可變報頭中是否需要包含這些字段。

poYBAGGzOrqAdWzrAAD4p-euB-8009.png

圖3.2 connect報文組成

16.3.1.1 connect固定報頭

bit 7 6 5 4 3 2 1 0
Byte1 Mqtt報文類型(1) Reserved(保留位)
0 0 0 1 0 0 0 0
Byte2~n 剩余長度

表格3.1

16.3.1.2 協(xié)議名字節(jié)組成

說明 7 6 5 4 3 2 1 0
協(xié)議名
Byte1 協(xié)議名長度MSB(0) 0 0 0 0 0 0 0 0
Byte2 協(xié)議名長度LSB(4) 0 0 0 0 0 1 0 0
Byte3 ‘M’ 0 1 0 0 1 1 0 1
Byte4 ‘Q’ 0 1 0 1 0 0 0 1
Byte5 ‘T’ 0 1 0 1 0 1 0 0
Byte6 ‘T’ 0 1 0 1 0 1 0 0

數(shù)據(jù)包檢測工具, 例如防火墻, 可以使用協(xié)議名來識別 MQTT 流量。

16.3.1.3 協(xié)議級別

說明 7 6 5 4 3 2 1 0
協(xié)議級別
Byte7 Level(4) 0 0 0 0 0 1 0 0

客戶端用 8 位的無符號值表示協(xié)議的修訂版本。對于 3.1.1 版協(xié)議,協(xié)議級別字段的值是 4(0x04)。如果發(fā)現(xiàn)不支持的協(xié)議級別,服務(wù)端必須給發(fā)送一個返回碼為 0x01(不支持的協(xié)議級別)的CONNACK 報文響應(yīng)CONNECT 報文, 然后斷開客戶端的連接。

16.3.1.4 連接標(biāo)記

用戶名標(biāo)記 用戶密碼標(biāo)記 Will retain Will qos Will flag 清除會話 reserved
Byte 8 x x x x x x 0
bit 7 6 5 4 3 2 1 0

bit1清除會話

一般來說, 客戶端連接時總是將清理會話標(biāo)志設(shè)置為 0 或 1, 并且不交替使用兩種值。 這個選擇取決于具體的應(yīng)用。 清理會話標(biāo)志設(shè)置為 1 的客戶端不會收到舊的應(yīng)用消息, 而且在每次連接成功后都需要重新訂閱任何相關(guān)的主題。清理會話標(biāo)志設(shè)置為 0 的客戶端會收到所有在它連接斷開期間發(fā)布的 QoS 1 和 QoS 2 級別的消息。因此, 要確保不丟失連接斷開期間的消息, 需要使用 QoS 1 或QoS 2 級別,同時將清理會話標(biāo)志設(shè)置為 0。

Bit2遺囑標(biāo)志

遺囑標(biāo)志(Will Flag) 被設(shè)置為 1,表示如果連接請求被接受了, 遺囑(Will Message) 消息必須被存儲在服務(wù)端并且與這個網(wǎng)絡(luò)連接關(guān)聯(lián)。之后網(wǎng)絡(luò)連接關(guān)閉時,服務(wù)端必須發(fā)布這個遺囑消息, 除非服務(wù)端收到DISCONNECT 報文時刪除了這個遺囑消息。

Bit3和 bit4遺囑 QoS

這兩位用于指定發(fā)布遺囑消息時使用的服務(wù)質(zhì)量等級, 如果遺囑標(biāo)志被設(shè)置為 0, 遺囑 QoS 也必須設(shè)置為 0(0x00),如果遺囑標(biāo)志被設(shè)置為 1, 遺囑 QoS 的值可以等于 0(0x00), 1(0x01), 2(0x02), 它的值不能等于 3。

Bit5遺囑保留

如果遺囑消息被發(fā)布時需要保留,需要指定這一位的值, 如果遺囑標(biāo)志被設(shè)置為 0, 遺囑保留(Will Retain) 標(biāo)志也必須設(shè)置為 0 。
如果遺囑標(biāo)志被設(shè)置為 1:
· 如果遺囑保留被設(shè)置為 0, 服務(wù)端必須將遺囑消息當(dāng)作非保留消息發(fā)布 。
· 如果遺囑保留被設(shè)置為 1, 服務(wù)端必須將遺囑消息當(dāng)作保留消息發(fā)布。

Bit7用戶名標(biāo)志

如果用戶名(User Name) 標(biāo)志被設(shè)置為 0, 有效載荷中不能包含用戶名字段。

如果用戶名(User Name) 標(biāo)志被設(shè)置為 1, 有效載荷中必須包含用戶名字段。

Bit6用戶名密碼標(biāo)記

如果密碼(Password) 標(biāo)志被設(shè)置為 0, 有效載荷中不能包含密碼字段 。
如果密碼(Password) 標(biāo)志被設(shè)置為 1, 有效載荷中必須包含密碼字段 。
如果用戶名標(biāo)志被設(shè)置為 0, 密碼標(biāo)志也必須設(shè)置為 0 。

16.3.1.5 保持連接

bit 7 6 5 4 3 2 1 0
Byte9 保持連接 Keep Alive MSB
Byte10 保持連接 Keep Alive LSB

a) 保持連接(Keep Alive) 是一個以秒為單位的時間間隔,表示為一個 16 位的字,它是指在客戶端傳輸完成。

b) 一個控制報文的時刻到發(fā)送下一個報文的時刻, 兩者之間允許空閑的最大時間間隔。 客戶端負(fù)責(zé)保證控制。

c) 報文發(fā)送的時間間隔不超過保持連接的值。 如果沒有任何其它的控制報文可以發(fā)送, 客戶端必須發(fā)送一個PINGREQ 報文。

d) 不管保持連接的值是多少,客戶端任何時候都可以發(fā)送 PINGREQ 報文,并且使用 PINGRESP 報文判斷網(wǎng)絡(luò)和服務(wù)端的活動狀態(tài)。

e) 如果保持連接的值非零,并且服務(wù)端在一點五倍的保持連接時間內(nèi)沒有收到客戶端的控制報文, 它必須斷開客戶端的網(wǎng)絡(luò)連接, 認(rèn)為網(wǎng)絡(luò)連接已斷開。

f) 客戶端發(fā)送了 PINGREQ 報文之后, 如果在合理的時間內(nèi)仍沒有收到 PINGRESP 報文, 它應(yīng)該關(guān)閉到服務(wù)端的網(wǎng)絡(luò)連接。

g) 保持連接的值為零表示關(guān)閉保持連接功能。 這意味著,服務(wù)端不需要因為客戶端不活躍而斷開連接。 注意:不管保持連接的值是多少, 任何時候,只要服務(wù)端認(rèn)為客戶端是不活躍或無響應(yīng)的, 可以斷開客戶端的連接。

16.3.1.6 客戶端標(biāo)識符

服務(wù)端使用客戶端標(biāo)識符 (ClientId) 識別客戶端。 連接服務(wù)端的每個客戶端都有唯一的客戶端標(biāo)識符(ClientId) ??蛻舳撕头?wù)端都必須使用 ClientId 識別兩者之間的 MQTT 會話相關(guān)的狀態(tài), 客戶端標(biāo)識符 (ClientId) 必須存在而且必須是 CONNECT 報文有效載荷的第一個字段,客戶端標(biāo)識符必須是UTF-8 編碼字符串。

16.3.1.7 遺囑主題

如果遺囑標(biāo)志被設(shè)置為 1, 有效載荷的下一個字段是遺囑主題(Will Topic) 。 遺囑主題必須是 UTF-8 編碼字符串。

16.3.1.8 遺囑消息

如果遺囑標(biāo)志被設(shè)置為 1, 有效載荷的下一個字段是遺囑消息。 遺囑消息定義了將被發(fā)布到遺囑主題的應(yīng)用消息。

16.3.1.9 用戶名和密碼

如果用戶名( User Name) 標(biāo)志被設(shè)置為 1, 有效載荷的下一個字段就是它。 用戶名必須是定義的UTF-8 編碼字符串。服務(wù)端可以將它用于身份驗證和授權(quán)。

如果密碼( Password) 標(biāo)志被設(shè)置為 1, 有效載荷的下一個字段就是它。密碼字段包含一個兩字節(jié)的長度字段, 長度表示二進(jìn)制數(shù)據(jù)的字節(jié)數(shù)( 不包含長度字段本身占用的兩個字節(jié)),后面跟著 0 到 65535 字節(jié)的二進(jìn)制數(shù)據(jù)。

pYYBAGGzOrqAEGAWAAA_goSeZQQ703.png

圖3.2 用戶名和密碼在connect報文中的組成

16.3.10.1 wirshark抓包分析connect報文

從抓包可知,從上到下分別是固定報頭,可變報頭,連接標(biāo)記,保持連接,用戶名,用名密碼,其中沒有遺囑相關(guān)消息字段,與3.1.1節(jié)分析的固定報頭組成分析一致。

poYBAGGzOruALhghAAFZfL_zoXE234.png

圖 3.3使用wireshark抓包分析connect報文組成格式

16.3.10.2 c語言構(gòu)造mqtt connect報文

static uint8_t client_id[512] = {"mqtt_client"}; static uint8_t user_name[512] = {"mqtt"}; static uint8_t passwd[512] = {"12345678"}; #define KEEP_ALIVE 20 int mqtt_connect(int sockfd) { uint8 flags = 0x00; uint8 *packet = NULL; uint16 packet_length = 0; uint16 clientidlen = strlen(client_id); uint16 usernamelen = strlen(user_name); uint16 passwordlen = strlen(passwd); uint16 payload_len = clientidlen + 2; // Variable header uint8 var_header[10] = { 0x00,0x04,/*len*/ 0x4d,0x51,0x54,0x54,/*mqtt*/ 0x04,/*協(xié)議版本*/}; uint8 fixedHeaderSize = 2; // Default size = one byte Message Type + one byte Remaining Length uint8 remainLen = 0; uint8 *fixed_header = NULL; uint16 offset = 0; // Preparing the flags if(usernamelen) { /*用戶名長度(可選)*/ payload_len += usernamelen + 2; flags |= MQTT_USERNAME_FLAG;/*或上用戶名標(biāo)記*/ } if(passwordlen) { /*用戶密碼(可選)*/ payload_len += passwordlen + 2; flags |= MQTT_PASSWORD_FLAG;/*用戶密碼標(biāo)記位*/ } flags |= MQTT_CLEAN_SESSION; var_header[7] = flags;/*連接標(biāo)記*/ var_header[8] = KEEP_ALIVE>>8;/*保持連接字段,占用兩個字節(jié)*/ var_header[9] = KEEP_ALIVE&0xFF; remainLen = sizeof(var_header)+payload_len; /*剩余長度,也就是可變報頭加上負(fù)載的長度*/ if (remainLen > 127) { fixedHeaderSize++;// add an additional byte for Remaining Length } fixed_header = (uint8 *)malloc(fixedHeaderSize); /*固定報頭*/ // Message Type *fixed_header = MQTT_MSG_CONNECT;/*報文類型,connect*/ if (remainLen <= 127) {// Remaining Length,剩余長度計算,可變長編碼 *(fixed_header+1) = remainLen; } else { // first byte is remainder (mod) of 128, then set the MSB to indicate more bytes *(fixed_header+1) = remainLen % 128; *(fixed_header+1) = *(fixed_header+1) | 0x80; // second byte is number of 128s *(fixed_header+2) = remainLen / 128; } packet_length = fixedHeaderSize+sizeof(var_header)+payload_len;/*固定報頭+可變報頭+負(fù)載長度*/ packet = (uint8 *)malloc(packet_length);/*分配內(nèi)存*/ memset(packet, 0, packet_length); memcpy(packet, fixed_header, fixedHeaderSize);/*填充固定報頭*/ free(fixed_header); offset += fixedHeaderSize; memcpy(packet+offset, var_header, sizeof(var_header));/*填充可變報頭*/ offset += sizeof(var_header); packet[offset++] = clientidlen>>8;// Client ID - UTF encoded,填充clientid長度+clientid packet[offset++] = clientidlen&0xFF; memcpy(packet+offset, client_id, clientidlen); offset += clientidlen; if(usernamelen) {// Username - UTF encoded,填充用戶名+用戶名長度 packet[offset++] = usernamelen>>8; packet[offset++] = usernamelen&0xFF; memcpy(packet+offset, user_name, usernamelen); offset += usernamelen; } if(passwordlen) {// Password - UTF encoded,填充用戶密碼+用戶名密碼長度 packet[offset++] = passwordlen>>8; packet[offset++] = passwordlen&0xFF; memcpy(packet+offset, passwd, passwordlen); offset += passwordlen; } // Send the packet if (client_send(sockfd,packet, packet_length) < 0){ free(packet); return -1; } free(packet); return 1; }

16.3.2 CONNACK-確認(rèn)連接請求

服務(wù)端發(fā)送 CONNACK 報文響應(yīng)從客戶端收到的 CONNECT 報文。服務(wù)端發(fā)送給客戶端的第一個報文必須是 CONNACK。

16.3.2.1 固定報頭

bit 7 6 5 4 3 2 1 0
Byte1 MQTT 控制報文類型 (2) Reserved 保留位
0 0 1 0 0 0 0 0
Byte2 剩余長度
0 0 0 0 0 0 1 0

剩余長度字段表示可變報頭的長度。 對于 CONNACK 報文這個值等于 2。

16.3.2.2 可變報頭

描述 7 6 5 4 3 2 1 0
連接確認(rèn)標(biāo)記 保留位 SP1
Byte1 0 0 0 0 0 0 0 X
連接返回碼
Byte2 x x x x x x x x

Byte1,Bit0連接確認(rèn)標(biāo)志

位 7-1 是保留位且必須設(shè)置為 0,

對于bit0,如果服務(wù)端收到一個 CleanSession 為 0 的連接, 當(dāng)前會話標(biāo)志的值取決于服務(wù)端是否已經(jīng)保存了 ClientId對應(yīng)客戶端的會話狀態(tài)。 如果服務(wù)端已經(jīng)保存了會話狀態(tài), 它必須將 CONNACK 報文中的當(dāng)前會話標(biāo)志設(shè)置為 1 。 如果服務(wù)端沒有已保存的會話狀態(tài), 它必須將 CONNACK 報文中的當(dāng)前會話設(shè)置為 0。 還需要將 CONNACK 報文中的返回碼設(shè)置為 0。

連接返回碼

如果服務(wù)端發(fā)送了一個包含非零返回碼的 CONNACK 報文, 那么它必須關(guān)
閉網(wǎng)絡(luò)連接。

0 0x00 連接已被服務(wù)端接受
1 0x01 服務(wù)端不支持客戶端請求的協(xié)議版本
2 0x02 客戶端標(biāo)識符是正確的 UTF-8 編碼, 但服務(wù) 端不允許使用
3 0x03 網(wǎng)絡(luò)連接已建立, 但 MQTT 服務(wù)不可用
4 0x04 用戶名或密碼的數(shù)據(jù)格式無效
5 0x05 客戶端未被授權(quán)連接到此服務(wù)器
6-255 保留
返回碼響應(yīng) 描述

CONNACK沒有有效載荷。

16.3.2.3 CONNACK報文wireshark抓包分析

pYYBAGGzOsGAHt38AABY_FbZl6c369.png

圖3.4 CONNACK 抓包報文

16.3.2.4 c語言構(gòu)造connect ack報文

void mqtt_connect_ack(int sockfd) { uint8_t cmd[]={ 0x20/*報文類型*/, 0x02/*剩余長度*/ ,0x00,0x00/*最后兩個字節(jié)可變報頭表示返回狀態(tài)碼*/ }; send_msg(sockfd,cmd,sizeof(cmd)); socket_record_t *socket_record = look_up_by_sokfd(sockfd); if(socket_record==NULL){ return; } socket_record->is_connect=0x01; }

16.3.3 PUBLISH-發(fā)布消息

PUBLISH 控制報文是指從客戶端向服務(wù)端或者服務(wù)端向客戶端傳輸一個應(yīng)用消息。

poYBAGGzOseAVCQtAABkswXSRKs395.png

圖 3.5 publish報文組成格式

16.3.3.1 固定報頭

bit 7 6 5 4 3 2 1 0
Byte 1 MQTT報文類型(3) dup Qos等級 RETAIN
0 0 1 1 x x x x
Byte2 剩余長度

Bit3 dup

如果 DUP 標(biāo)志被設(shè)置為 0, 表示這是客戶端或服務(wù)端第一次請求發(fā)送這個 PUBLISH 報文。 如果 DUP 標(biāo)志被設(shè)置為 1,表示這可能是一個早前報文請求的重發(fā)??蛻舳嘶蚍?wù)端請求重發(fā)一個 PUBLISH 報文時, 必須將 DUP 標(biāo)志設(shè)置為 1.。 對于 QoS0 的消息, DUP 標(biāo)志必須設(shè)置為 0。

Bit1和bit2 qos等級

0 0 0 最多分發(fā)一次
1 0 1 至少分發(fā)一次
2 1 0 只分發(fā)一次
- 1 1 保留不使用
Qos值 bit2 bit1 描述

qos由發(fā)送端決定,發(fā)送端發(fā)送什么qos的消息,接收端就回復(fù)什么qos的消息。

pYYBAGGzOs2AFQm9AACpcGqPY4s186.png

不同qos等級mqtt報文交互流程

Bit0保留標(biāo)記位

一般設(shè)置為0。

剩余長度

等于可變報頭的長度加上有效載荷的長度。

可變報頭

可變報頭按順序包含主題名和標(biāo)識符。主題,用于識別有效載荷數(shù)據(jù)應(yīng)該被發(fā)布到哪一個信息通道,標(biāo)識符,只有當(dāng) QoS 等級是 1 或 2 時,報文標(biāo)識符( Packet Identifier) 字段才能出現(xiàn)在 PUBLISH 報文中。

16.3.3.2 抓包分析PUBLISH報文

poYBAGGzOtOAMC2WAACMX12OT68100.png

圖 3.6 PUBLISH 抓包報文

16.3.3.3 構(gòu)造publish 報文

int mqtt_publish_with_qos(int sockfd,const char* topic, const char* msg, uint16 msgl, uint8 retain, uint8 qos, uint16* message_id) { socket_record_t *socket_record = look_up_by_sokfd(sockfd); if(NULL == socket_record){ return -1; } DEBUG_INFO("sockfd:%d",socket_record->sockfd); uint16 topiclen = strlen(topic); uint16 msglen = msgl; uint8 *var_header = NULL; // Topic size (2 bytes), utf-encoded topic uint8 *fixed_header = NULL; uint8 fixedHeaderSize = 0,var_headerSize = 0; // Default size = one byte Message Type + one byte Remaining Length uint16 remainLen = 0; uint8 *packet = NULL; uint16 packet_length = 0; uint8 qos_flag = MQTT_QOS0_FLAG; /*qos標(biāo)記*/ uint8 qos_size = 0; // No QoS included if(qos == 1) { qos_size = 2; // 2 bytes for QoS qos_flag = MQTT_QOS1_FLAG; } else if(qos == 2) { qos_size = 2; // 2 bytes for QoS qos_flag = MQTT_QOS2_FLAG; } // Variable header var_headerSize = topiclen/*主題內(nèi)容*/+2/*主題長度占用兩字節(jié)*/+qos_size/*標(biāo)識符*/; var_header = (uint8 *)malloc(var_headerSize); memset(var_header, 0, var_headerSize); *var_header = topiclen>>8; *(var_header+1) = topiclen&0xFF; memcpy(var_header+2, topic, topiclen); if(qos_size) {//qos1和qos2的報文需要填充標(biāo)識符,有點像tcp的seq socket_record->publish_seq++; if(socket_record->publish_seq == 0){ //unsigned short 表示范圍0~65535,標(biāo)識符必須是非零整數(shù) socket_record->publish_seq = 1; } var_header[topiclen+2] = (socket_record->publish_seq & 0xff00)>>8; var_header[topiclen+3] = socket_record->publish_seq & 0x00ff; if(message_id) { *message_id = socket_record->publish_seq; } } fixedHeaderSize = 2; // Default size = one byte Message Type + one byte Remaining Length remainLen = var_headerSize+msglen; if (remainLen > 127) {/*剩余長度*/ fixedHeaderSize++; // add an additional byte for Remaining Length } fixed_header = (uint8 *)malloc(fixedHeaderSize);/*固定報頭+剩余長度*/ // Message Type, DUP flag, QoS level, Retain *fixed_header = MQTT_MSG_PUBLISH | qos_flag;/*報文類型和qos標(biāo)記*/ if(retain) { *fixed_header |= MQTT_RETAIN_FLAG;/*是否保留*/ } // Remaining Length,剩余長度 if (remainLen <= 127) { *(fixed_header+1) = remainLen; } else { // first byte is remainder (mod) of 128, then set the MSB to indicate more bytes *(fixed_header+1) = remainLen % 128; *(fixed_header+1) = *(fixed_header+1) | 0x80; // second byte is number of 128s *(fixed_header+2) = remainLen / 128; } packet_length = fixedHeaderSize+var_headerSize+msglen;/*固定報頭+可變報頭+負(fù)載長度*/ packet = (uint8 *)malloc(packet_length); memset(packet, 0, packet_length); memcpy(packet, fixed_header, fixedHeaderSize);/*填充固定報頭*/ memcpy(packet+fixedHeaderSize, var_header, var_headerSize);/*填充可變報頭*/ memcpy(packet+fixedHeaderSize+var_headerSize, msg, msglen);/*負(fù)載*/ free(var_header); free(fixed_header); send_msg(sockfd,packet , packet_length); free(packet); return 1; }

16.3.4 PUBREC-發(fā)布收到

PUBREC 報文是對 QoS 等級 2 的 PUBLISH 報文的響應(yīng)。它是 QoS 2 等級協(xié)議交換的第二個報文。

16.3.4.1 固定報頭

bit 7 6 5 4 3 2 1 0
Byte 1 MQTT報文類型(5) 保留位
0 1 0 1 0 0 0 0
Byte2 剩余長度

剩余長度

表示可變報頭的長度。 對 PUBREC 報文它的值等于 2。

可變報頭

bit 7 6 5 4 3 2 1 0
Byte1 報文標(biāo)識符MSB
Byte2 報文標(biāo)識符LSB

有效載荷

PUBREC 報文沒有有效載荷。

16.3.4.2 PUBREC抓包報文

pYYBAGGzOtiACPMOAAA3fmTL1oQ105.png

圖 3.7 PUBREC抓包報文圖示

16.3.4.3 c語言構(gòu)造pubrec報文

//如果是PUBREC報文,head_type=0x50 void mqtt_qos2_pubrec(int sockfd , unsigned char *data,unsigned char head_type) { uint16 msg_id = mqtt_parse_msg_id(data);/*報文標(biāo)識符,回復(fù)報文和接受報文的標(biāo)識符必須一樣*/ unsigned char qos2_pubrec_respon[]={head_type/*固定報頭*/,0x02/*剩余長度*/, (msg_id&0xff00)>>8 , msg_id&0x00ff/*最后兩個字節(jié)是報文標(biāo)識符*/}; send_msg(sockfd,qos2_pubrec_respon,sizeof(qos2_pubrec_respon)); }

16.3.5 PUBREL-發(fā)布釋放

PUBREL 報文是對 PUBREC 報文的響應(yīng)。 它是 QoS 2 等級協(xié)議交換的第三個報文。

16.3.5.1 固定報頭

bit 7 6 5 4 3 2 1 0
Byte 1 MQTT報文類型(6) 保留位
0 1 1 0 0 0 0 0
Byte2 剩余長度

剩余長度

表示可變報頭的長度。 對 PUBREL 報文它的值等于 2。

可變報頭

bit 7 6 5 4 3 2 1 0
Byte1 報文標(biāo)識符MSB
Byte2 報文標(biāo)識符LSB

有效載荷

PUBREL 報文沒有有效載荷。

16.3.5.2 PUBREL抓包報文

poYBAGGzOt6ASFeNAAA09LDGivU386.png

圖 3.8 PUBREL抓包報文圖示

16.3.5.3 c語言構(gòu)造pubrel報文

//head_type=0x62 void mqtt_qos2_pubrel(int sockfd , unsigned char *data,unsigned char head_type) { uint16 msg_id = mqtt_parse_msg_id(data); unsigned char qos2_pubrel_respon[]={head_type/*報文類型*/,0x02/*剩余長度*/, (msg_id & 0xff00)>>8 , msg_id & 0x00ff/*最后兩個字節(jié)是報文標(biāo)識符*/}; send_msg(sockfd,qos2_pubrel_respon,sizeof(qos2_pubrel_respon)); }

16.3.6 PUBCOMP-發(fā)布完成

PUBCOMP 報文是對 PUBREL 報文的響應(yīng)。 它是 QoS 2 等級協(xié)議交換的第四個也是最后一個報文。

16.3.6.1 固定報頭

bit 7 6 5 4 3 2 1 0
Byte 1 MQTT報文類型(7) 保留位
0 1 1 1 0 0 0 0
Byte2 剩余長度

剩余長度

表示可變報頭的長度。 對 PUBCOMP 報文它的值等于 2。

可變報頭

bit 7 6 5 4 3 2 1 0
Byte1 報文標(biāo)識符MSB
Byte2 報文標(biāo)識符LSB

有效載荷

PUBCOMP 報文沒有有效載荷。

16.3.6.2 PUBCOMP抓包報文

pYYBAGGzOuSACFmjAAA6NB9m4qk558.png

圖 3.9 PUBCOMP抓包報文圖示

16.3.6.3 c語言構(gòu)造pubcom報文

//head_type=0x70 void mqtt_qos2_pubcomp(int sockfd , unsigned char *data,unsigned char head_type) { uint16 msg_id = mqtt_parse_msg_id(data);/*報文標(biāo)識符*/ unsigned char qos2_pubcomp_respon[]={head_type/*報文類型*/,0x02/*剩余長度*/, (msg_id & 0xff00)>>8 , msg_id & 0x00ff/*最后兩個字節(jié)報文標(biāo)識符*/}; send_msg(sockfd,qos2_pubcomp_respon,sizeof(qos2_pubcomp_respon)); }

16.3.7 PINGREQ-心跳請求

客戶端發(fā)送 PINGREQ 報文給服務(wù)端的。用于:

a) 在沒有任何其它控制報文從客戶端發(fā)給服務(wù)的時,告知服務(wù)端客戶端還活著。

b) 請求服務(wù)端發(fā)送 響應(yīng)確認(rèn)它還活著。

c) 使用網(wǎng)絡(luò)以確認(rèn)網(wǎng)絡(luò)連接沒有斷開。

16.3.7.1 固定報頭

bit 7 6 5 4 3 2 1 0
Byte 1 MQTT報文類型(12) 保留位
1 1 0 0 0 0 0 0
Byte2 剩余長度 0

可變報頭

報文沒有可變報頭。

有效載荷

PINGREQ 報文沒有有效載荷。

16.3.7.2 PINGREQ 抓包報文

poYBAGGzOuWAW5i7AAAvOJBfs98472.png

圖 3.10 PINGREQ抓包報文圖示

16.3.7.3 c語言構(gòu)造pingreq報文

int mqtt_ping(int sockfd) { uint8 packet[] = {MQTT_MSG_PINGREQ/*報文類型*/,0x00/*剩余長度*/}; int ret = send_msg(sockfd,packet, sizeof(packet)); return ret; }

16.3.8 PINGRESP – 心跳響應(yīng)

服務(wù)端發(fā)送 PINGRESP 報文響應(yīng)客戶端的 PINGREQ 報文。 表示服務(wù)端還活著。

16.3.8.1 固定報頭

bit 7 6 5 4 3 2 1 0
Byte 1 MQTT報文類型(13) 保留位
1 1 0 1 0 0 0 0
Byte2 剩余長度 0

可變報頭

報文沒有可變報頭。

有效載荷

PINGRESP 報文沒有有效載荷。

16.3.8.2 PINGRESP 抓包報文

pYYBAGGzOuaACWYzAAAtcUGorAk127.png

圖 3.11 PINGRESP抓包報文圖示

16.3.8.3 c語言構(gòu)造pinpresp報文

void mqtt_ping_req_reply(int sockfd) { uint8_t cmd[]={0xd0/*報文類型*/, 0x00/*剩余長度*/}; send_msg(sockfd,cmd,sizeof(cmd)); }

16.3.9 DISCONNECT –斷開連接

DISCONNECT 報文是客戶端發(fā)給服務(wù)端的最后一個控制報文。表示客戶端正常斷開連接。

16.3.9.1 固定報頭

bit 7 6 5 4 3 2 1 0
Byte 1 MQTT報文類型(14) 保留位
1 1 1 0 0 0 0 0
Byte2 剩余長度 0

可變報頭

DISCONNECT報文沒有可變報頭。

有效載荷

DISCONNECT 報文沒有有效載荷。

16.3.9.2 DISCONNECT 抓包報文

poYBAGGzOuaAcWyMAAAc4e9-WMM369.png

圖 3.12 DISCONNECT抓包報文圖示

16.3.9.3 c語言構(gòu)造disconnect報文

int mqtt_disconnect(int sockfd) { uint8 packet[] = {MQTT_MSG_DISCONNECT/*報文類型*/,0x00/*剩余長度*/}; int ret = client_send(sockfd,packet, sizeof(packet)); DEBUG_INFO("ret=%d",ret); return ret; }

16.3.10 SUBSCRIBE-訂閱主題

客戶端向服務(wù)端發(fā)送 SUBSCRIBE 報文用于創(chuàng)建一個或多個訂閱。 每個訂閱注冊客戶端關(guān)心的一個或多個主題。 為了將應(yīng)用消息轉(zhuǎn)發(fā)給與那些訂閱匹配的主題, 服務(wù)端發(fā)送 PUBLISH 報文給客戶端。 SUBSCRIBE報文也(為每個訂閱)指定了最大的 QoS 等級, 服務(wù)端根據(jù)這個發(fā)送應(yīng)用消息給客戶端。

pYYBAGGzOuaAed3BAABoUAaCGJQ640.png

圖 3.13 訂閱主題報文組成格式

16.3.10.1 固定報頭

bit 7 6 5 4 3 2 1 0
Byte 1 MQTT報文類型(8) 保留位
1 1 1 0 0 0 0 0
Byte2 剩余長度 0

SUBSCRIBE 控制報固定報頭的第 3,2,1,0 位是保留位, 必須分別設(shè)置為 0,0,1,0,服務(wù)端必須將其它的任何值都當(dāng)做是不合法的并關(guān)閉網(wǎng)絡(luò)連接。

剩余長度字段
等于可變報頭的長度( 2 字節(jié)) 加上有效載荷的長度。

可變報頭

bit 7 6 5 4 3 2 1 0
Byte1 報文標(biāo)識符MSB
Byte2 報文標(biāo)識符LSB

服務(wù)端收到客戶端發(fā)送的一個 SUBSCRIBE 報文時, 必須使用 SUBACK 報文響應(yīng)。SUBACK 報文必須和等待確認(rèn)的 SUBSCRIBE 報文有相同的報文標(biāo)識符。

有效載荷

7 6 5 4 3 2 1 0
主題
Byte1 主題長度MSB
Byte2 主題長度LSB
Byte3~n 主題
服務(wù)質(zhì)量
保留 qos等級
ByteN+1 0 0 0 0 0 0 x x

QoS 不等于 0,1 或 2, 服務(wù)端必須認(rèn)為 SUBSCRIBE 報文是不合法的并關(guān)閉網(wǎng)絡(luò)連接。

16.3.10.2 SUBSCRIBE報文抓包

poYBAGGzOueAECCgAABX9ua5mis828.png

圖 3.14 訂閱主題抓包報文

16.3.10.3 c語言構(gòu)造subscribe報文

static uint16 su_seq = 1; int mqtt_subscribe_theme(int sockfd,char *Theme , uint8_t Qos) { su_seq++;//報文標(biāo)識符 if(su_seq == 0){ su_seq = 1; } uint16_t MessageId = su_seq; uint8_t cmd[1024]={0}; //報文標(biāo)示符長度2 + 主題長度位占用2字節(jié)+主題內(nèi)容+qos標(biāo)識 int data_length = 2+2+strlen(Theme)+1; int playload_len = strlen(Theme); uint8_t len_byte[4] ={0x00 , 0x00 ,0x00 ,0x00}; uint8_t byte_num = length_trans_byte_form(data_length , len_byte);/*把剩余長度轉(zhuǎn)換成變長編碼*/ cmd[0] = 0x82; memcpy(&cmd[1] , len_byte , byte_num); cmd[1+byte_num]=(MessageId & 0xff00) >> 8 ; cmd[1+byte_num+1] = MessageId & 0x00ff; cmd[1+byte_num+1+1] = (playload_len & 0xff00) >> 8; cmd[1+byte_num+1+1+1] = playload_len & 0x00ff; memcpy(&cmd[1+byte_num+1+1+1+1] , Theme , playload_len); cmd[1+byte_num+1+1+1+1+playload_len] = Qos; client_send(sockfd,cmd, 1+byte_num+1+1+1+1+playload_len+1); }

16.3.11 SUBACK –訂閱確認(rèn)

服務(wù)端發(fā)送 SUBACK 報文給客戶端,用于確認(rèn)它已收到并且正在處理 SUBSCRIBE 報文。

16.3.11.1 固定報頭

bit 7 6 5 4 3 2 1 0
Byte 1 MQTT報文類型(9) 保留位
1 0 0 1 0 0 0 0
Byte2 剩余長度 0

剩余長度字段
等于可變報頭的長度加上有效載荷的長度。

可變報頭

bit 7 6 5 4 3 2 1 0
Byte1 報文標(biāo)識符MSB
Byte2 報文標(biāo)識符LSB

可變報頭包含等待確認(rèn)的 SUBSCRIBE 報文的報文標(biāo)識符。

3.11.2 SUBACK抓包報文

pYYBAGGzOueAaK09AABFOV_QNYg978.png

圖 3.15訂閱主題ack抓包報文

16.3.11.3 c語言構(gòu)造suback報文

void mqtt_subscribe_ack(int sockfd,const uint8 *buf) { uint16 msg_id = mqtt_parse_msg_id(buf);/*提取報文標(biāo)識符*/ uint8 qos = MQTTParseMessageQos(buf);/*提取報文qos*/ uint8 cmd[]={0x90,0x03/*剩余長度*/, (msg_id & 0xff00) >> 8, msg_id & 0x00ff,qos}; send_msg(sockfd,cmd,sizeof(cmd)); }

16.3.12 UNSUBSCRIBE –取消訂閱

客戶端發(fā)送 UNSUBSCRIBE 報文給服務(wù)端, 用于取消訂閱主題。

poYBAGGzOueAXTgkAABeCdXmxdQ126.png

圖 3.16取消訂閱主題報文結(jié)構(gòu)

16.3.12.1 固定報頭

bit 7 6 5 4 3 2 1 0
Byte 1 MQTT報文類型(10) 保留位
1 0 1 0 0 0 0 0
Byte2 剩余長度 0

UNSUBSCRIBE 報文固定報頭的第 3,2,1,0 位是保留位且必須分別設(shè)置為 0,0,1,0。 服務(wù)端必須認(rèn)為任何其它的值都是不合法的并關(guān)閉網(wǎng)絡(luò)連接。

剩余長度字段
等于可變報頭的長度,加上有效載荷的長度。

可變報頭

bit 7 6 5 4 3 2 1 0
Byte1 報文標(biāo)識符MSB
Byte2 報文標(biāo)識符LSB

可變報頭包含一個報文標(biāo)識符。

有效載荷

7 6 5 4 3 2 1 0
主題1
Byte1 主題長度MSB
Byte2 主題長度LSB
Byte3~n 主題
主題2

UNSUBSCRIBE 報文的有效載荷必須至少包含一個消息過濾器。 沒有有效載荷的 UNSUBSCRIBE 報文是違反協(xié)議的。

16.3.12.2 UNSUBSCRIBE抓包報文

pYYBAGGzOueAXDUPAABIi_K_mdk278.png

圖 3.17取消訂閱主題抓包報文

16.3.12.3 c語言構(gòu)造unsubscribe報文

static uint16 un_seq = 1; int mqtt_unsubscribe_theme(int sockfd,const char* topic) { un_seq++; if(un_seq == 0){ un_seq = 1; } uint16_t MessageId = un_seq; uint8_t cmd[1024]={0}; //報文標(biāo)示符長度2 + 主題長度位占用2字節(jié)+主題內(nèi)容+qos標(biāo)識 int data_length = 2+2+strlen(topic)+1; int playload_len = strlen(topic); uint8_t len_byte[4] ={0x00 , 0x00 ,0x00 ,0x00}; uint8_t byte_num = length_trans_byte_form(data_length , len_byte);/*剩余長度轉(zhuǎn)換成變長編碼*/ cmd[0] = 0xa2; memcpy(&cmd[1] , len_byte , byte_num); cmd[1+byte_num]=(MessageId & 0xff00) >> 8 ; cmd[1+byte_num+1] = MessageId & 0x00ff; cmd[1+byte_num+1+1] = (playload_len & 0xff00) >> 8; cmd[1+byte_num+1+1+1] = playload_len & 0x00ff; memcpy(&cmd[1+byte_num+1+1+1+1] , topic , playload_len); client_send(sockfd,cmd,1+byte_num+1+1+1+1+playload_len+1); return 1; }

16.3.13 UNSUBACK –取消訂閱確認(rèn)

服務(wù)端發(fā)送 UNSUBACK 報文給客戶端用于確認(rèn)收到 UNSUBSCRIBE 報文。

poYBAGGzOuiAQyKBAAA2dGXo_GI317.png

圖 3.18取消訂閱主題ack報文組成

16.3.13.1 固定報頭

bit 7 6 5 4 3 2 1 0
Byte 1 MQTT報文類型(11) 保留位
1 0 1 1 0 0 0 0
Byte2 剩余長度 0

剩余長度字段
表示可變報頭的長度, 對 UNSUBACK 報文這個值等于 2。

可變報頭

bit 7 6 5 4 3 2 1 0
Byte1 報文標(biāo)識符MSB
Byte2 報文標(biāo)識符LSB

可變報頭包含等待確認(rèn)的 UNSUBSCRIBE 報文的報文標(biāo)識符。

16.3.12.2 UNSUBSCRIBE ACK抓包報文

pYYBAGGzOuiAVQbAAAAtHofI4HE667.png

圖 3.19取消訂閱主題ACK抓包報文

16.3.12.3 c語言構(gòu)造unsubscribe報文

void mqtt_unsubscribe_ack(int sockfd,const uint8 *buf) { uint16 msg_id = mqtt_parse_msg_id(buf); uint8 cmd[]={0xb0,0x02/*剩余長度*/,(msg_id & 0xff00) >> 8, msg_id & 0x00ff/*最后兩個字節(jié)是報文標(biāo)識符*/}; send_msg(sockfd,cmd,sizeof(cmd)); }

16.3.14 服務(wù)端與客戶端交互操作過程

16.3.14.1 編譯

編譯client之前先在代碼中指定server ip

poYBAGGzOumAZQ5DAAAhVOJZDk4261.png

進(jìn)入client目錄,直接make即可

pYYBAGGzOumAXhcCAAA5CTEXKyA175.png

進(jìn)入server目錄,直接make即可

poYBAGGzOuqAaJE5AAA6FHswyt0142.png

16.3.14.2 執(zhí)行

先運(yùn)行server

pYYBAGGzOuqAC_LIAAAmGnNxgxY363.png

再運(yùn)行client

poYBAGGzOuuAJmRCAABB_76Atiw904.png

Client操作流程

pYYBAGGzOuuAAAqkAADHpC_fbpw046.png

在server端查看

poYBAGGzOuyACXD0AAC6PH2v9V4367.png



審核編輯黃昊宇

聲明:本文內(nèi)容及配圖由入駐作者撰寫或者入駐合作網(wǎng)站授權(quán)轉(zhuǎn)載。文章觀點僅代表作者本人,不代表電子發(fā)燒友網(wǎng)立場。文章及其配圖僅供工程師學(xué)習(xí)之用,如有內(nèi)容侵權(quán)或者其他違規(guī)問題,請聯(lián)系本站處理。 舉報投訴
  • Linux
    +關(guān)注

    關(guān)注

    88

    文章

    11760

    瀏覽量

    219042
  • MQTT
    +關(guān)注

    關(guān)注

    5

    文章

    733

    瀏覽量

    25091
收藏 人收藏
加入交流群
微信小助手二維碼

掃碼添加小助手

加入工程師交流群

    評論

    相關(guān)推薦
    熱點推薦

    DR1平臺Linux應(yīng)用開發(fā)指南:含GDB調(diào)試、Python及MQTT實戰(zhàn)

    本文為創(chuàng)龍科技DR1 系列評估板 Linux 應(yīng)用開發(fā)手冊,系統(tǒng)講解開發(fā)環(huán)境搭建、GDB 調(diào)試、多類型應(yīng)用案例及 MQTT 通信實現(xiàn)。核心內(nèi)容包括 LinuxSDK 與工具鏈配置、GD
    的頭像 發(fā)表于 01-05 16:48 ?4577次閱讀
    DR1平臺<b class='flag-5'>Linux</b>應(yīng)用<b class='flag-5'>開發(fā)</b>指南:含GDB調(diào)試、Python及<b class='flag-5'>MQTT</b>實戰(zhàn)

    科倫特亮相第十六屆CBDS中國商用顯示系統(tǒng)產(chǎn)業(yè)領(lǐng)袖峰會

    2025年12月18日,第十六屆CBDS中國商用顯示系統(tǒng)產(chǎn)業(yè)領(lǐng)袖峰會在深圳凱賓斯基酒店盛大舉行。作為深圳市商用顯示系統(tǒng)產(chǎn)業(yè)促進(jìn)會常務(wù)副會長單位,科倫特受邀出席本次峰會系列活動,與來自海內(nèi)外的行業(yè)領(lǐng)袖、專家學(xué)者及企業(yè)代表齊聚一堂,圍繞商用顯示產(chǎn)業(yè)的技術(shù)演進(jìn)、應(yīng)用創(chuàng)新與全球化發(fā)展路徑展開深入交流。
    的頭像 發(fā)表于 12-28 10:56 ?633次閱讀

    第十六屆亞洲電源技術(shù)發(fā)展論壇 | 芯海科技揭秘“新國標(biāo)”移動電源BMS方案

    12月6日,由21世紀(jì)電源網(wǎng)、電子研習(xí)社聯(lián)合主辦的“第十六屆亞洲電源技術(shù)發(fā)展論壇”在深圳隆重舉辦,來自半導(dǎo)體芯片、功率器件、人型機(jī)器人、機(jī)器狗、智能終端等技術(shù)方向,超3000名工程師齊聚一堂,共同
    的頭像 發(fā)表于 12-09 09:11 ?911次閱讀
    <b class='flag-5'>第十六</b>屆亞洲電源技術(shù)發(fā)展論壇 | 芯??萍冀颐亍靶聡鴺?biāo)”移動電源BMS方案

    MQTT網(wǎng)關(guān)對接到物聯(lián)網(wǎng)平臺快速開發(fā)應(yīng)用

    且可擴(kuò)展的物聯(lián)網(wǎng)解決方案的基礎(chǔ)框架,為從設(shè)備數(shù)據(jù)采集、傳輸?shù)浇K端系統(tǒng)應(yīng)用的全流程提供技術(shù)支撐。 1、數(shù)據(jù)采集、協(xié)議分析 通過MQTT智能網(wǎng)關(guān)的協(xié)議
    的頭像 發(fā)表于 11-03 11:13 ?415次閱讀

    創(chuàng)龍 瑞芯微 RK3588 國產(chǎn)2.4GHz八核 工業(yè)開發(fā)板—MQTT通信協(xié)議案例

    本文圍繞創(chuàng)龍科技研發(fā)的評估板,提供 MQTT 通信協(xié)議開發(fā)案例指導(dǎo)。涵蓋 MQTT 協(xié)議核心概念,詳解案例功能(如數(shù)據(jù)發(fā)布與訂閱)、環(huán)境搭建
    的頭像 發(fā)表于 10-28 15:23 ?2493次閱讀
    創(chuàng)龍 瑞芯微 RK3588 國產(chǎn)2.4GHz八核 工業(yè)<b class='flag-5'>開發(fā)</b>板—<b class='flag-5'>MQTT</b>通信<b class='flag-5'>協(xié)議</b>案例

    Air780EPM嵌入式開發(fā):LuatOS下的MQTT通信實踐

    通過LuatOS腳本在Air780EPM開發(fā)板上實現(xiàn)MQTT通信,是物聯(lián)網(wǎng)設(shè)備開發(fā)中高效且便捷的解決方案。 一、MQTT 協(xié)議詳解 ? 1.
    的頭像 發(fā)表于 09-30 16:11 ?1828次閱讀
    Air780EPM嵌入式<b class='flag-5'>開發(fā)</b>:LuatOS下的<b class='flag-5'>MQTT</b>通信實踐

    國產(chǎn)!全志T113-i 雙核Cortex-A7@1.2GHz 工業(yè)開發(fā)板—MQTT通信協(xié)議案例

    -5.4.61、Linux-RT-5.4.61 本文主要介紹創(chuàng)龍科技TLT113-EVM評估板基于MQTT通信協(xié)議開發(fā)案例,主要包括mqtt
    的頭像 發(fā)表于 07-31 14:34 ?787次閱讀
    國產(chǎn)!全志T113-i 雙核Cortex-A7@1.2GHz 工業(yè)<b class='flag-5'>開發(fā)</b>板—<b class='flag-5'>MQTT</b>通信<b class='flag-5'>協(xié)議</b>案例

    第十六章 W55MH32 PING示例

    本文講解了如何在 W55MH32?芯片上通過 IPRAW?模式實現(xiàn) ICMP?協(xié)議中的 PING?命令,以進(jìn)行網(wǎng)絡(luò)連通性測試,通過實戰(zhàn)例程展示了從發(fā)送 PING?請求、接收并解析回復(fù)到統(tǒng)計結(jié)果的完整
    的頭像 發(fā)表于 07-24 11:41 ?977次閱讀
    <b class='flag-5'>第十六章</b> W55MH32 PING示例

    簡析Modbus和MQTT協(xié)議

    Modbus和MQTT協(xié)議在設(shè)計目標(biāo)、通信模式、應(yīng)用場景、網(wǎng)絡(luò)結(jié)構(gòu)、數(shù)據(jù)傳輸效率、設(shè)備兼容性及安全性等方面存在顯著差異,具體分析如下: 一、設(shè)計目標(biāo)與定位 Modbus :誕生于1979年,由施耐德
    的頭像 發(fā)表于 07-10 14:25 ?780次閱讀

    高德紅外亮相第十六屆北京光博會

    近日,第十六屆北京光博會在北京國家會議中心盛大開幕。高德紅外攜全產(chǎn)業(yè)鏈布局下的最新成果與尖端產(chǎn)品精彩亮相,展示了紅外熱成像技術(shù)在千行百業(yè)的廣闊應(yīng)用。
    的頭像 發(fā)表于 07-01 10:23 ?1386次閱讀

    揚(yáng)杰科技榮獲第十六屆投資者關(guān)系管理天馬獎

    近日,證券時報社第十六屆上市公司投資者關(guān)系管理論壇暨2025中國城市發(fā)展新質(zhì)生產(chǎn)力巡禮在揚(yáng)州盛大舉行?;顒蝇F(xiàn)場,備受矚目的第十六屆上市公司投資者關(guān)系管理天馬獎獲獎名單正式揭曉。 作為國內(nèi)主流財經(jīng)媒體
    的頭像 發(fā)表于 06-16 18:07 ?718次閱讀

    卡特彼勒亮相第十六屆國際基礎(chǔ)設(shè)施投資與建設(shè)高峰論壇

    第十六屆 國際基礎(chǔ)設(shè)施投資與建設(shè)高峰論壇于今日在澳門隆重啟幕,本屆論壇以“更好互聯(lián)互通,更多合作共贏”為主題,旨在交流全球基礎(chǔ)設(shè)施互聯(lián)互通的熱點和前沿話題,探討綠色化、數(shù)字化、智能化技術(shù)在基礎(chǔ)設(shè)施互聯(lián)互通中的最新應(yīng)用和實踐。
    的頭像 發(fā)表于 06-13 11:50 ?956次閱讀

    “智小虎”數(shù)字營銷策略大模型榮獲第十六屆虎嘯獎

    近日,第十六屆虎嘯獎頒獎典禮在上海舉行,智子科技聯(lián)合南京大學(xué)、虎嘯獎共同研發(fā)的 "智小虎" 數(shù)字營銷策略大模型,獲評 "年度最佳 AIGC 系統(tǒng)/工具/平臺"。這是對智小虎在數(shù)字營銷領(lǐng)域技術(shù)落地能力和行業(yè)實踐價值的權(quán)威認(rèn)可。
    的頭像 發(fā)表于 06-11 15:48 ?817次閱讀

    達(dá)實智能亮相第十六屆中國數(shù)據(jù)中心大會

    近日,第十六屆中國數(shù)據(jù)中心大會成功召開,達(dá)實智能參與的前海信息樞紐大廈數(shù)據(jù)中心項目,被評為“2024 年度數(shù)據(jù)中心實施樣板項目”,達(dá)實智能榮獲“2024年度數(shù)據(jù)中心工程企業(yè)30強(qiáng)”稱號!
    的頭像 發(fā)表于 03-27 15:28 ?1015次閱讀

    中星微技術(shù)亮相第十六屆中國數(shù)據(jù)中心大會

    近日,主題為“算力破陣 慧見未來”的第十六屆中國數(shù)據(jù)中心大會在北京隆重召開,作為一站式高效(機(jī)房)密評方案提供商,中星微技術(shù)股份有限公司(以下簡稱“中星微技術(shù)”)出席大會,并以“打造數(shù)據(jù)安全智聯(lián)
    的頭像 發(fā)表于 03-26 14:11 ?874次閱讀