——
[1.MQTT項(xiàng)目工程](https://github.com/LiamBindle/MQTT-C)
[2.MQTT API說(shuō)明文檔](https://liambindle.ca/MQTT-C/group__api.html)
[3.MQTT協(xié)議中文版](https://mcxiaoke.gitbooks.io/mqtt-cn/content/mqtt/01-Introduction.html)
1. MQTT概述
MQTT是一個(gè)客戶端服務(wù)端架構(gòu)的發(fā)布/訂閱模式的消息傳輸協(xié)議。它的設(shè)計(jì)思想是輕巧、開放、簡(jiǎn)單、規(guī)范,易于實(shí)現(xiàn)。這些特點(diǎn)使得它對(duì)很多場(chǎng)景來(lái)說(shuō)都是很好的選擇,特別是對(duì)于受限的環(huán)境如機(jī)器與機(jī)器的通信(M2M)以及物聯(lián)網(wǎng)環(huán)境(IoT)。
2. MQTT控制報(bào)文格式
MQTT協(xié)議通過(guò)交換預(yù)定義的MQTT控制報(bào)文來(lái)通信。
報(bào)文格式:固定包頭+可變包頭+payload。
固定包頭:由兩個(gè)字節(jié)組成
????????????????byte1 高四位表示控制報(bào)文的類型,低四位表示控制報(bào)文類型的標(biāo)志位。
? ? ? ? ? ? ? ? byte2表示剩余長(zhǎng)度,包括可變報(bào)頭和負(fù)載的數(shù)據(jù)。剩余長(zhǎng)度不包括用于編碼剩余長(zhǎng)度字段本身的字節(jié)數(shù)。
可變包頭:
????????????????某些MQTT控制報(bào)文包含一個(gè)可變報(bào)頭部分。它在固定報(bào)頭和負(fù)載之間??勺儓?bào)頭的內(nèi)容根據(jù)報(bào)文類型的不同而不同??勺儓?bào)頭的報(bào)文標(biāo)(Packet Identifier)字段存在于在多個(gè)類型的報(bào)文里。
有效載荷:
? ??????????????對(duì)于PUBLISH來(lái)說(shuō)有效載荷就是應(yīng)用消息。
3. MQTT控制報(bào)文
3.1 連接服務(wù)器
——1. 網(wǎng)絡(luò)建立連接后,客戶端發(fā)送的第一個(gè)報(bào)文必須是CONNECT報(bào)文
——2. 客戶端只能發(fā)送一次CONNECT 報(bào)文,發(fā)送兩次會(huì)當(dāng)作協(xié)議違規(guī)處理,并斷開連接。
——3. 有效載荷包括:客戶端唯一標(biāo)識(shí)符,will主題,will消息,用戶名和密碼。
——4.可變包頭:協(xié)議名(protocol name)+協(xié)議級(jí)別(protocol level)+連接標(biāo)志(connect flags)+保持連接(keep alive)。
——5. 協(xié)議名: MQTT的UTF-8編碼的字符串。(MSB+LSB +MQTT 六個(gè)字節(jié))
——6. 協(xié)議級(jí)別:一個(gè)字節(jié)
——7. 連接標(biāo)志:一個(gè)字節(jié),包含一些用于指定MQTT連接行為的參數(shù)。同時(shí)還指出有效載荷中的字段是否存在。服務(wù)端必須驗(yàn)證CONNECT控制報(bào)文的保留標(biāo)志位(第0位)是否為0,如果不為0必須斷開客戶端連接。
——8. 清理會(huì)話:byte8 的bit1位標(biāo)志。
? ? ? ? 這個(gè)二進(jìn)制位指定了會(huì)話狀態(tài)的處理方式??蛻舳撕头?wù)端可以保存會(huì)話狀態(tài),以支持跨網(wǎng)絡(luò)連接的可靠消息傳輸。這個(gè)標(biāo)志位用于控
? ? ? ?制會(huì)話狀態(tài)的生存時(shí)間。
? ? ? ? 標(biāo)志設(shè)置為0:服務(wù)端必須基于當(dāng)前會(huì)話(使用客戶端標(biāo)識(shí)符識(shí)別)的狀態(tài)恢復(fù)與客戶端的通信。
? ? ? ? 標(biāo)志設(shè)置為1:客戶端和服務(wù)端必須丟棄之前的任何會(huì)話并開始一個(gè)新的會(huì)話。會(huì)話僅持續(xù)和網(wǎng)絡(luò)連接同樣長(zhǎng)的時(shí)間。
——9. 遺囑標(biāo)志 WILL FLAG: byte8的bit2位標(biāo)志。
????????遺囑標(biāo)志(Will Flag)被設(shè)置為1,表示如果連接請(qǐng)求被接受了,遺囑(Will Message)消息必須被存儲(chǔ)在服務(wù)端并且與這個(gè)網(wǎng)絡(luò)連接關(guān)聯(lián)。之后網(wǎng)絡(luò)連接關(guān)閉時(shí),服務(wù)端必須發(fā)布這個(gè)遺囑消息,除非服務(wù)端收到DISCONNECT報(bào)文時(shí)刪除了這個(gè)遺囑消息。
3.2 ?確認(rèn)連接請(qǐng)求
服務(wù)端發(fā)送CONNACK報(bào)文響應(yīng)從客戶端收到的CONNECT報(bào)文。服務(wù)端發(fā)送給客戶端的第一個(gè)報(bào)文必須是CONNACK。
如果客戶端在合理的時(shí)間內(nèi)沒有收到服務(wù)端的CONNACK報(bào)文,客戶端應(yīng)該關(guān)閉網(wǎng)絡(luò)連接。合理 的時(shí)間取決于應(yīng)用的類型和通信基礎(chǔ)設(shè)施。
CONNACK報(bào)文沒有有效載荷。
3.3 發(fā)布消息
PUBLISH控制報(bào)文是指從客戶端向服務(wù)端或者服務(wù)端向客戶端傳輸一個(gè)應(yīng)用消息。
固定包頭:
注意byte1的bit3為重發(fā)標(biāo)志DUP。
如果DUP標(biāo)志被設(shè)置為0,表示這是客戶端或服務(wù)端第一次請(qǐng)求發(fā)送這個(gè)PUBLISH報(bào)文。如果DUP標(biāo)志被設(shè)置為1,表示這可能是一個(gè)早前報(bào)文請(qǐng)求的重發(fā)。
服務(wù)端發(fā)送PUBLISH報(bào)文給訂閱者時(shí),收到(入站)的PUBLISH報(bào)文的DUP標(biāo)志的值不會(huì)被傳播。發(fā)送(出站)的PUBLISH報(bào)文與收到(入站)的PUBLISH報(bào)文中的DUP標(biāo)志是獨(dú)立設(shè)置的,它的值必須單獨(dú)的根據(jù)發(fā)送(出站)的PUBLISH報(bào)文是否是一個(gè)重發(fā)來(lái)確定。
可變包頭:
主題名:topic name
報(bào)文標(biāo)識(shí)符:packet identitfier。
有效載荷:有效載荷包含將被發(fā)布的應(yīng)用消息。數(shù)據(jù)的內(nèi)容和格式是應(yīng)用特定的。有效載荷的長(zhǎng)度這樣計(jì)算:用固定報(bào)頭中的剩余長(zhǎng)度字段的值減去可變報(bào)頭的長(zhǎng)度。包含零長(zhǎng)度有效載荷的PUBLISH報(bào)文是合法的。
響應(yīng):PUBLISH報(bào)文的接收者必須按照根據(jù)PUBLISH報(bào)文中的QoS等級(jí)發(fā)送響應(yīng)。
3.4 發(fā)布確認(rèn)
PUBACK報(bào)文是對(duì)QoS 1等級(jí)的PUBLISH報(bào)文的響應(yīng)。
PUBACK報(bào)文沒有有效載荷。
3.5 發(fā)布收到
PUBREC報(bào)文是對(duì)QoS等級(jí)2的PUBLISH報(bào)文的響應(yīng)。它是QoS 2等級(jí)協(xié)議交換的第二個(gè)報(bào)文。
PUBREC報(bào)文沒有有效載荷。
3.6 發(fā)布釋放
PUBREL報(bào)文是對(duì)PUBREC報(bào)文的響應(yīng)。它是QoS 2等級(jí)協(xié)議交換的第三個(gè)報(bào)文。
PUBREL報(bào)文沒有有效載荷。
3.7 發(fā)布完成
PUBCOMP報(bào)文是對(duì)PUBREL報(bào)文的響應(yīng)。它是QoS 2等級(jí)協(xié)議交換的第四個(gè)也是最后一個(gè)報(bào)文。
PUBCOMP報(bào)文沒有有效載荷。
3.8 訂閱主題
客戶端向服務(wù)端發(fā)送SUBSCRIBE報(bào)文用于創(chuàng)建一個(gè)或多個(gè)訂閱。每個(gè)訂閱注冊(cè)客戶端關(guān)心的一個(gè)或多個(gè)主題。為了將應(yīng)用消息轉(zhuǎn)發(fā)給與那些訂閱匹配的主題,服務(wù)端發(fā)送PUBLISH報(bào)文給客戶端。SUBSCRIBE報(bào)文也(為每個(gè)訂閱)指定了最大的QoS等級(jí),服務(wù)端根據(jù)這個(gè)發(fā)送應(yīng)用消息給客戶端。
有效載荷:
SUBSCRIBE報(bào)文的有效載荷包含了一個(gè)主題過(guò)濾器列表,它們表示客戶端想要訂閱的主題。SUBSCRIBE報(bào)文的有效載荷必須包含至少一對(duì)主題過(guò)濾器 和 QoS等級(jí)字段組合。沒有有效載荷的SUBSCRIBE報(bào)文是違反協(xié)議的。
響應(yīng):
服務(wù)端收到客戶端發(fā)送的一個(gè)SUBSCRIBE報(bào)文時(shí),必須使用SUBACK報(bào)文響應(yīng),SUBACK報(bào)文必須和等待確認(rèn)的SUBSCRIBE報(bào)文有相同的報(bào)文標(biāo)識(shí)符。
3.9 訂閱確認(rèn)
服務(wù)端發(fā)送SUBACK報(bào)文給客戶端,用于確認(rèn)它已收到并且正在處理SUBSCRIBE報(bào)文。SUBACK報(bào)文包含一個(gè)返回碼清單,它們指定了SUBSCRIBE請(qǐng)求的每個(gè)訂閱被授予的最大QoS等級(jí)。
有效載荷:
有效載荷包含一個(gè)返回碼清單。每個(gè)返回碼對(duì)應(yīng)等待確認(rèn)的SUBSCRIBE報(bào)文中的一個(gè)主題過(guò)濾器。返回碼的順序必須和SUBSCRIBE報(bào)文中主題過(guò)濾器的順序相同。
3.10 取消訂閱
客戶端發(fā)送UNSUBSCRIBE報(bào)文給服務(wù)端,用于取消訂閱主題。
有效載荷:
UNSUBSCRIBE報(bào)文的有效載荷包含客戶端想要取消訂閱的主題過(guò)濾器列表。
UNSUBSCRIBE報(bào)文中的主題過(guò)濾器必須是連續(xù)打包的、按照定義的UTF-8編碼字符串?
UNSUBSCRIBE報(bào)文的有效載荷必須至少包含一個(gè)消息過(guò)濾器。沒有有效載荷的UNSUBSCRIBE報(bào)文是違反協(xié)議的。
響應(yīng):
UNSUBSCRIBE報(bào)文提供的主題過(guò)濾器(無(wú)論是否包含通配符)必須與服務(wù)端持有的這個(gè)客戶端的當(dāng)前主題過(guò)濾器集合逐個(gè)字符比較。如果有任何過(guò)濾器完全匹配,那么它(服務(wù)端)自己的訂閱將被刪除,否則不會(huì)有進(jìn)一步的處理。
如果服務(wù)端刪除了一個(gè)訂閱:
——它必須停止分發(fā)任何新消息給這個(gè)客戶端 []。
——它必須完成分發(fā)任何已經(jīng)開始往客戶端發(fā)送的QoS 1和QoS 2的消息 []。
——它可以繼續(xù)發(fā)送任何現(xiàn)存的準(zhǔn)備分發(fā)給客戶端的緩存消息。
????????????服務(wù)端必須發(fā)送UNSUBACK報(bào)文響應(yīng)客戶端的UNSUBSCRIBE請(qǐng)求。UNSUBACK報(bào)文必須包含和UNSUBSCRIBE報(bào)文相同的報(bào)文標(biāo)識(shí)符 。即使沒有刪除任何主題訂閱,服務(wù)端也必須發(fā)送一個(gè)UNSUBACK響應(yīng) 。
????????????如果服務(wù)端收到包含多個(gè)主題過(guò)濾器的UNSUBSCRIBE報(bào)文,它必須如同收到了一系列的多個(gè)UNSUBSCRIBE報(bào)文一樣處理那個(gè)報(bào)文,除了將它們的響應(yīng)合并到一個(gè)單獨(dú)的UNSUBACK報(bào)文外。?
3.11 取消訂閱確認(rèn)
服務(wù)端發(fā)送UNSUBACK報(bào)文給客戶端用于確認(rèn)收到UNSUBSCRIBE報(bào)文。
UNSUBACK報(bào)文沒有有效載荷。
3.12 心跳請(qǐng)求
客戶端發(fā)送PINGREQ報(bào)文給服務(wù)端的。用于:
1. 在沒有任何其它控制報(bào)文從客戶端發(fā)給服務(wù)的時(shí),告知服務(wù)端客戶端還活著。
2. 請(qǐng)求服務(wù)端發(fā)送 響應(yīng)確認(rèn)它還活著。
3. 使用網(wǎng)絡(luò)以確認(rèn)網(wǎng)絡(luò)連接沒有斷開。
保持連接(Keep Alive)處理中用到這個(gè)報(bào)文。
——PINGREQ報(bào)文沒有可變報(bào)頭。
——PINGREQ報(bào)文沒有有效載荷。
響應(yīng):
服務(wù)端必須發(fā)送 PINGRESP報(bào)文響應(yīng)客戶端的PINGREQ報(bào)文。
3.13 心跳響應(yīng)
服務(wù)端發(fā)送PINGRESP報(bào)文響應(yīng)客戶端的PINGREQ報(bào)文。表示服務(wù)端還活著。
保持連接(Keep Alive)處理中用到這個(gè)報(bào)文。
PINGRESP報(bào)文沒有可變報(bào)頭。
PINGRESP報(bào)文沒有有效載荷。
3.14 斷開連接
DISCONNECT報(bào)文是客戶端發(fā)給服務(wù)端的最后一個(gè)控制報(bào)文。表示客戶端正常斷開連接。
DISCONNECT報(bào)文沒有可變報(bào)頭。
DISCONNECT報(bào)文沒有有效載荷。
響應(yīng):
客戶端發(fā)送DISCONNECT報(bào)文之后:
—— 必須關(guān)閉網(wǎng)絡(luò)連接 [MQTT-3.14.4-1]。
——不能通過(guò)那個(gè)網(wǎng)絡(luò)連接再發(fā)送任何控制報(bào)文。
服務(wù)端在收到DISCONNECT報(bào)文時(shí):
——必須丟棄任何與當(dāng)前連接關(guān)聯(lián)的未發(fā)布的遺囑消息。
——應(yīng)該關(guān)閉網(wǎng)絡(luò)連接,如果客戶端 還沒有這么做。
4. 操作行為
4.1 狀態(tài)存儲(chǔ)
為了提供服務(wù)質(zhì)量保證,客戶端和服務(wù)端有必要存儲(chǔ)會(huì)話狀態(tài)。在整個(gè)會(huì)話期間,客戶端和服務(wù)端都必須存儲(chǔ)會(huì)話狀態(tài) 。會(huì)話必須至少持續(xù)和它的活躍網(wǎng)絡(luò)連接同樣長(zhǎng)的時(shí)間。服務(wù)端的保留消息不是會(huì)話狀態(tài)的組成部分。服務(wù)端應(yīng)該保留那種消息直到客戶端刪除它。
4.2 網(wǎng)絡(luò)連接
MQTT協(xié)議要求基礎(chǔ)傳輸層能夠提供有序的、可靠的、雙向傳輸(從客戶端到服務(wù)端 和從服務(wù)端到客戶端)的字節(jié)流。
無(wú)連接的網(wǎng)絡(luò)傳輸協(xié)議如UDP是不支持的,因?yàn)樗麄兛赡軙?huì)丟失數(shù)據(jù)包或?qū)?shù)據(jù)包重排序。
4.3 服務(wù)質(zhì)量等級(jí)和協(xié)議流程
MQTT按照這里定義的服務(wù)質(zhì)量 (QoS) 等級(jí)分發(fā)應(yīng)用消息。分發(fā)協(xié)議是對(duì)稱的,在下面的描述中,客戶端和服務(wù)端既可以是發(fā)送者也可以是接收者。分發(fā)協(xié)議關(guān)注的是從單個(gè)發(fā)送者到單個(gè)接收者的應(yīng)用消息。服務(wù)端分發(fā)應(yīng)用消息給多個(gè)客戶端時(shí),每個(gè)客戶端獨(dú)立處理。分發(fā)給客戶端的出站應(yīng)用消息和入站應(yīng)用消息的QoS等級(jí)可能是不同的。
qos0:最多分發(fā)一次
qos1:至少分發(fā)一次
qos2:僅分發(fā)一次
4.4 消息分發(fā)重試
客戶端設(shè)置清理會(huì)話(CleanSession)標(biāo)志為0重連時(shí),客戶端和服務(wù)端必須使用原始的報(bào)文標(biāo)識(shí)符重發(fā)任何未確認(rèn)的PUBLISH報(bào)文(如果QoS>0)和PUBREL報(bào)文 [MQTT-4.4.0-1]。這是唯一要求客戶端或服務(wù)端重發(fā)消息的情況。
4.5 消息收到
服務(wù)端接管入站應(yīng)用消息的所有權(quán)時(shí),它必須將消息添加到訂閱匹配的客戶端的會(huì)話狀態(tài)。正常情況下,客戶端收到發(fā)送給它的訂閱的消息??蛻舳艘部赡苁盏讲皇桥c它的訂閱精確匹配的消息。如果服務(wù)端自動(dòng)給客戶端分配了一個(gè)訂閱,可能發(fā)生這種情況。正在處理UBSUBSCRIBE請(qǐng)求時(shí)也可能收到消息。客戶端必須按照可用的服務(wù)質(zhì)量(QoS)規(guī)則確認(rèn)它收到的任何PUBLISH報(bào)文,不管它選擇是否處理報(bào)文包含的應(yīng)用消息 。
4.6 消息排序
實(shí)現(xiàn)本章定義的協(xié)議流程時(shí),客戶端必須遵循下列規(guī)則:
重發(fā)任何之前的PUBLISH報(bào)文時(shí),必須按原始PUBLISH報(bào)文的發(fā)送順序重發(fā)(適用于QoS 1和QoS 2消息)[MQTT-4.6.0-1]。
——必須按照對(duì)應(yīng)的PUBLISH報(bào)文的順序發(fā)送PUBACK報(bào)文(QoS 1消息)。
——必須按照對(duì)應(yīng)的PUBLISH報(bào)文的順序發(fā)送PUBREC報(bào)文(QoS 2消息。
——必須按照對(duì)應(yīng)的PUBREC報(bào)文的順序發(fā)送PUBREL報(bào)文(QoS 2消息)。
服務(wù)端必須默認(rèn)認(rèn)為每個(gè)主題都是有序的。它可以提供一個(gè)管理功能或其它機(jī)制,以允許將一個(gè)或多個(gè)主題當(dāng)作是無(wú)序的 。
服務(wù)端處理發(fā)送給有序主題的消息時(shí),必須按照上面的規(guī)則將消息分發(fā)給每個(gè)訂閱者。此外,它必須按照從客戶端收到的順序發(fā)送PUBLISH報(bào)文給消費(fèi)者(對(duì)相同的主題和QoS)。
4.7 主題名和主題過(guò)濾器
斜杠(‘/’ U+002F)用于分割主題的每個(gè)層級(jí),為主題名提供一個(gè)分層結(jié)構(gòu).
數(shù)字標(biāo)志(‘#’ U+0023)是用于匹配主題中任意層級(jí)的通配符。
加號(hào) (‘+’ U+002B) 是只能用于單個(gè)主題層級(jí)匹配的通配符。
服務(wù)端不能將 $ 字符開頭的主題名匹配通配符 (#或+) 開頭的主題過(guò)濾器.
$SYS/ 被廣泛用作包含服務(wù)器特定信息或控制接口的主題的前綴。
應(yīng)用不能使用 $ 字符開頭的主題。
訂閱 “#” 的客戶端不會(huì)收到任何發(fā)布到以 “$” 開頭主題的消息。
訂閱 “+/monitor/Clients” 的客戶端不會(huì)收到任何發(fā)布到 “$SYS/monitor/Clients” 的消息。
訂閱 “$SYS/#” 的客戶端會(huì)收到發(fā)布到以 “$SYS/” 開頭主題的消息。
訂閱 “$SYS/monitor/+” 的客戶端會(huì)收到發(fā)布到 “$SYS/monitor/Clients” 主題的消息。
如果客戶端想同時(shí)接受以 “$SYS/” 開頭主題的消息和不以 $ 開頭主題的消息,它需要同
時(shí)訂閱 “#” 和 ““$SYS/#”。
4.8 錯(cuò)誤處理
除非另有說(shuō)明,如果服務(wù)端或客戶端遇到了協(xié)議違規(guī)的行為,它必須關(guān)閉傳輸這個(gè)協(xié)議違規(guī)控制報(bào)文的網(wǎng)絡(luò)連接.
5 安全(非規(guī)范化)
MQTT方案通常部署在不安全的通信環(huán)境中。在這種情況下,協(xié)議實(shí)現(xiàn)通常需要提供這些機(jī)制:
——用戶和設(shè)備身份認(rèn)證
——服務(wù)端資源訪問授權(quán)
——MQTT控制報(bào)文和內(nèi)嵌應(yīng)用數(shù)據(jù)的完整性校驗(yàn)
——MQTT控制報(bào)文和內(nèi)嵌應(yīng)用數(shù)據(jù)的隱私控制
作為傳輸層協(xié)議,MQTT僅關(guān)注消息傳輸,提供合適的安全功能是實(shí)現(xiàn)者的責(zé)任。使用TLS[RFC5246] 是比較普遍的選擇。
廣泛采用高級(jí)加密標(biāo)準(zhǔn) [AES] 數(shù)據(jù)加密標(biāo)準(zhǔn) [DES] 。
推薦使用為受限的低端設(shè)備特別優(yōu)化過(guò)的輕量級(jí)加密國(guó)際標(biāo)準(zhǔn) ISO 29192 [ISO29192] 。
6. 使用web socket作為網(wǎng)絡(luò)層
如果MQTT在WebSocket [RFC6455] 連接上傳輸,必須滿足下面的條件:
——MQTT控制報(bào)文必須使用WebSocket二進(jìn)制數(shù)據(jù)幀發(fā)送。如果收到任何其它類型的數(shù)據(jù)幀,接收者必須關(guān)閉網(wǎng)絡(luò)連接 。
——單個(gè)WebSocket數(shù)據(jù)幀可以包含多個(gè)或者部分MQTT報(bào)文。接收者不能假設(shè)MQTT控制報(bào)文按WebSocket幀邊界對(duì)齊 。
——客戶端必須將字符串 mqtt 包含在它提供的WebSocket子協(xié)議列表里 。
——服務(wù)端選擇和返回的WebSocket子協(xié)議名必須是 ?。
——用于連接客戶端和服務(wù)器的WebSocket URI對(duì)MQTT協(xié)議沒有任何影響。
7. 一致性
MQTT規(guī)范定義了MQTT客戶端實(shí)現(xiàn)和MQTT服務(wù)端實(shí)現(xiàn)的一致性要求
MQTT實(shí)現(xiàn)可以同時(shí)是MQTT客戶端和MQTT服務(wù)端。接受入站連接和建立到其它服務(wù)端的出站連接的服務(wù)端必須同時(shí)符合MQTT客戶端和MQTT服務(wù)端的要求 。
為了與任何其它的一致性實(shí)現(xiàn)交互操作,一致性實(shí)現(xiàn)不能要求使用在本規(guī)范之外定義的任何擴(kuò)展 。
附錄:
控制報(bào)文類型

byte1:標(biāo)志位flags:
