學(xué)習(xí)MQTT協(xié)議。如果只是看了相關(guān)文檔就認(rèn)為可以了。那是一個(gè)錯(cuò)誤的觀念。筆者為了能更好的去理解MQTT協(xié)議??戳瞬簧傧嚓P(guān)的開源Broker的項(xiàng)目??上н@些項(xiàng)目一般都是不完全的。不過(guò)從這些項(xiàng)目中筆者至少發(fā)現(xiàn)他們大部都是通過(guò)Netty這個(gè)通信框架來(lái)完成的。哪怕是大型項(xiàng)目ActiveMQ也脫不了俗。特別是商用HiveMQ更是列為重要的一部分。所以筆者接下來(lái)會(huì)用Netty框架來(lái)實(shí)現(xiàn)一些代碼。這樣子有助于我們?nèi)ダ斫釳QTT協(xié)議。 本節(jié)筆者會(huì)來(lái)講連接報(bào)文(CONNECT)。可以說(shuō)他是所有報(bào)文的基礎(chǔ)。所有的動(dòng)作都必須在連接之上操作。我們都知道MQTT是基于TCP/IP網(wǎng)絡(luò)協(xié)議的。并以字節(jié)流傳輸?shù)?。他的行為?dòng)作更為簡(jiǎn)單。如下 我們可以知道連接會(huì)用到倆個(gè)報(bào)文類型類——CONNECT報(bào)文和CONNACK報(bào)文。其中CONNECT報(bào)文比較復(fù)雜一點(diǎn)??梢哉f(shuō)是所有報(bào)文中信息種類最多的。CONNACK報(bào)文的最大特點(diǎn)就是沒有有效載荷部分。 接下筆者就會(huì)講解一下連接的相關(guān)行為。同時(shí)也希望讀者們記住筆者這里講的一般是MQTT 3.1 和MQTT 3.1.1的協(xié)議。 CONNECT報(bào)文就是相當(dāng)連接請(qǐng)求一樣子。所以當(dāng)客戶端和服務(wù)端建立之后,服務(wù)端接受的第一份報(bào)文就必須是連接報(bào)文。相信這個(gè)不用筆者說(shuō)明也知道為什么。同時(shí)客戶端這邊要保證連接報(bào)文只能發(fā)送一次。同時(shí)在服務(wù)端也要有驗(yàn)證來(lái)自客戶端的連接報(bào)文只有一次。如果發(fā)送倆次以上的連接報(bào)文的話,不好意思請(qǐng)當(dāng)作違反了協(xié)議斷開當(dāng)前連接。從前面的圖片里面我們就知道如果客戶端發(fā)送一個(gè)連接報(bào)文(CONNECT)之后,服務(wù)端就會(huì)返回一個(gè)連接確定報(bào)文(CONNACK)。如果客戶端在一段時(shí)間之后,還沒有接收到來(lái)自服務(wù)端的連接確定報(bào)文(CONNACK)的話,客戶端一定要斷開連接。同時(shí)要重起一個(gè)新的網(wǎng)絡(luò)連接。在發(fā)一次連接報(bào)文(CONNECT)。 我們都知道控制報(bào)文分為固定報(bào)頭+可變報(bào)頭+有效載荷部分。其中可變報(bào)頭和有效載荷并不是必須要擁有的。而CONNECT報(bào)文卻是所有中筆者認(rèn)為最為復(fù)雜的報(bào)文。不管是可變報(bào)頭還是有效載荷部分他都擁有。 固定報(bào)頭 固定報(bào)頭的結(jié)構(gòu)上一單已講過(guò)了。占八個(gè)字節(jié)。從上一單的報(bào)文類型列表我們知道他的值為1。報(bào)文類型占四個(gè)字節(jié)。所以他的二進(jìn)制就是0001。在MQTT 3.1里面有講到DUP、QoS、 RETAIN 都沒有被用到。這些加起來(lái)占四個(gè)字節(jié)。這樣子的話筆者以為固定報(bào)頭最終的二進(jìn)制是0001。但是在MQTT 3.1.1里面卻又說(shuō)到以0保留了。所以最終二進(jìn)制是00010000。簡(jiǎn)單的講DUP、QoS、 RETAIN雖然沒有被用到,所以設(shè)置為0。 可變報(bào)頭 連接報(bào)文(CONNECT)的可變報(bào)頭的結(jié)果構(gòu)分為四個(gè)部分:協(xié)議名(Protocol Name)、協(xié)議等級(jí)(Protocol Level)、連接標(biāo)志(Connect Flags)、保持連接(Keep Alive)。其實(shí)協(xié)議名(Protocol Name)和協(xié)議等級(jí)(Protocol Level)筆者也不是很明白有什么用。只是知道MQTT 3.1 的協(xié)議名是MQIsdp,協(xié)議等級(jí)是3。而MQTT 3.1.1的協(xié)議名卻是MQTT,等級(jí)是4。好吧。至少說(shuō)明不同版本的MQTT協(xié)議存在不同的協(xié)議名和協(xié)議等級(jí)。這里面在文檔也有一個(gè)動(dòng)作存在。就是如果服務(wù)端發(fā)現(xiàn)協(xié)議名不正確的話,就必須斷開連接。如果是協(xié)議等級(jí)不對(duì)話,就是必須回返一個(gè)碼。 要問(wèn)可變報(bào)頭里面最重要的部分是哪一部分的話,筆者認(rèn)為是連接標(biāo)志(Connect Flags)。連接標(biāo)志里面包括用戶名標(biāo)志(User Name Flag)、密碼標(biāo)志(Password Flag)、遺囑保留(Will Retain)、清理會(huì)話(Clean Session)、保留(Reserved)等。他們的位置如圖下。
清理會(huì)話(CleanSession) 客戶端和服務(wù)端之間的通信時(shí)間,筆者認(rèn)為是一次會(huì)話。也就是在連接成功的時(shí)候?yàn)闀?huì)話開始。當(dāng)斷開連接的時(shí)候表示一次會(huì)話結(jié)束了。即然是會(huì)話,自然就有會(huì)話狀態(tài)。這些狀態(tài)當(dāng)前就是指通信之間的信息。如下 一、服務(wù)端的會(huì)話狀態(tài): 1)客戶端的訂閱信息。 2)已經(jīng)發(fā)送給客戶端,但是還沒有完成確認(rèn)的QoS 1和QoS 2級(jí)別的消息。 3)即將傳輸給客戶端的QoS 1和QoS 2級(jí)別的消息 4)已從客戶端接收,但是還沒有完成確認(rèn)的QoS 2級(jí)別的消息 5)準(zhǔn)備發(fā)送給客戶端的QoS 0級(jí)別的消息(可選) 二、客戶端的會(huì)話狀態(tài): 1)已經(jīng)發(fā)送給服務(wù)端,但是還沒有完成確認(rèn)的QoS 1和QoS 2級(jí)別的消息 2)已從服務(wù)端接收,但是還沒有完成確認(rèn)的QoS 2級(jí)別的消息 知道了會(huì)話狀態(tài),就可以明白清理會(huì)話就用來(lái)表示是否清除或是保存會(huì)話狀態(tài)。如果清理會(huì)話為0的話,表示要保存這里會(huì)話狀態(tài)。如果連接斷開了,在次連接的時(shí)候,服務(wù)要以什么來(lái)獲得已存在的會(huì)話狀態(tài)呢?客戶ID。這是在有效載荷分部分的下面會(huì)講到。根據(jù)客戶ID來(lái)找查有沒有相關(guān)的會(huì)話狀態(tài)存在。如果存在,就必須以存在的會(huì)話狀態(tài)來(lái)建立連接。如果沒有就創(chuàng)建一個(gè)新會(huì)話狀態(tài)。當(dāng)然斷開之后,不管是客戶端還是服務(wù)端都要保存當(dāng)前會(huì)話狀態(tài)。這個(gè)舉動(dòng)筆者認(rèn)為是為了數(shù)據(jù)重用吧。也可以說(shuō)是保證數(shù)據(jù)不會(huì)丟失。如果清理會(huì)話為1的話,事情就變的很簡(jiǎn)單。給我清除掉就是行了。沒有一次連接都是一個(gè)新的會(huì)話。 遺囑標(biāo)志(Will Flag) 關(guān)看到遺囑倆個(gè)字就應(yīng)該明白。用于表示在網(wǎng)絡(luò)連接突關(guān)閉的時(shí)候,要不要發(fā)送遺囑。遺囑標(biāo)志可關(guān)系遺囑QoS(Will QoS),遺囑保留(Will Retain),還有有效載荷里面的遺囑主題(Will Topic)和遺囑消息(Will Message)??梢哉f(shuō)遺囑標(biāo)志是遺囑功能總開關(guān)。只當(dāng)遺囑標(biāo)志為1的時(shí)候。說(shuō)明要用到遺囑功能。那遺囑QoS(Will QoS),遺囑保留(Will Retain)就可以被啟用。也就是說(shuō)遺囑主題(Will Topic)和遺囑消息(Will Message)必須要在有效載荷部分里面出現(xiàn)。 遺囑QoS(Will QoS) 他的值主要要看遺囑標(biāo)志(Will Flag)是的值。如果遺囑標(biāo)志(Will Flag)是1的話,遺囑QoS可以是0,1,2。這些值表示跟服務(wù)質(zhì)量是一樣子。如果遺囑標(biāo)志(Will Flag)是0的話,那么遺囑QoS必須也是0; 遺囑保留(Will Retain) 他的值也是要看遺囑標(biāo)志(Will Flag)是的值。如果遺囑標(biāo)志(Will Flag)是1的話,遺囑保留可以是0或1。用于表示有沒有做于保留消息發(fā)布; 用戶名標(biāo)志 User Name Flag 和 密碼標(biāo)志 Password Flag 筆者為什么把這倆個(gè)放在一起呢?相信做過(guò)很多業(yè)務(wù)開發(fā)的人都知道用戶名和密碼。上面這倆個(gè)就是用于標(biāo)示在有效載荷里有沒有相關(guān)的部分。比如。當(dāng)就用戶名標(biāo)示為1的時(shí)候,那就是說(shuō)明有效載荷部分里面有用戶名的信息。同理密碼標(biāo)示為1 就是表示有效載荷部分有密碼的信息。這里有一點(diǎn)要注意就是如果用戶名標(biāo)志為0的話,那密碼標(biāo)志就必須為0。有用戶名和密碼的話,我們就可以在服務(wù)端做一些身分驗(yàn)證的業(yè)務(wù)。同時(shí)還可以加入一些權(quán)限。 上面筆者講過(guò)可變報(bào)頭分為四個(gè)部分。剩下一個(gè)保持連接(Keep Alive)。保持連接(Keep Alive)表示在客戶端一個(gè)報(bào)文發(fā)送結(jié)束之后到下一次報(bào)文發(fā)送之前的空閑時(shí)間。單位為秒。記住是在客戶端而不是服務(wù)端。如果在保持連接的時(shí)間內(nèi)沒有發(fā)送任何報(bào)文的話。文檔里面是要求發(fā)送一個(gè)PINGREQ報(bào)文。通過(guò)他判斷服務(wù)端和客戶端之間的連接狀態(tài)。如果在一時(shí)間段接受不到?jīng)]有收到服務(wù)端發(fā)來(lái)PINGRESP報(bào)文,那么就應(yīng)該關(guān)閉于服務(wù)端的連接。前面講都是在客戶端這邊要做的事情。服務(wù)端這當(dāng)然也不能少。如果服務(wù)端判斷保持連接不為0的時(shí)候,就要以保持連接值的1.5陪時(shí)間來(lái)判斷是否有接到報(bào)文。如果沒有接受報(bào)文的話,就要關(guān)閉跟客戶端之間的連接。值得注意的事。服務(wù)端要斷開客戶端不是根據(jù)保持連接來(lái)處理。而是查看這個(gè)客戶端是不是閑了。如果是的話,什么時(shí)候都可以斷開連接的。 有效載荷 有效載荷事實(shí)上就是通信里面的用戶要存放的信息。只是這里面又不能全是用戶信息。還包括了一些MQTT需要的信息。這跟可變報(bào)頭的一些標(biāo)志有關(guān)系。但是不管什么樣子??蛻鬒D是必須在第一位的。后面就是遺囑主題和遺囑消息。最后才是用戶和密碼。 有效載荷 = 客戶ID + 遺囑主題 + 遺囑消息 + 用戶 + 密碼 為了方便學(xué)習(xí),筆者用軟件把包搞下來(lái)。上一章也講到過(guò)什么樣子搞下。如下圖下 上面的圖片算是比較全的連接報(bào)文。筆者也根據(jù)相應(yīng)的標(biāo)示出他們所在的位置。圖片下方是報(bào)文相應(yīng)的二進(jìn)制流。紅線標(biāo)出的數(shù)據(jù)是報(bào)文真時(shí)的數(shù)據(jù)對(duì)應(yīng)。而綠色線表示是接下來(lái)紅線相關(guān)數(shù)據(jù)的長(zhǎng)度。相信如果你看過(guò)MQTT相關(guān)的文檔就應(yīng)該知道MSB和LSB關(guān)鍵字的作用。那么圖片1紅線就是固定報(bào)頭,2紅線就是可變報(bào)頭。從3紅線開始后面都是有效載荷部分。 通上面我們大至能了解MQTT的連接過(guò)程要做些什么。在MQTT文檔里面有一點(diǎn)筆者有一點(diǎn)吃驚。筆者以為如果一個(gè)客戶端發(fā)送連接報(bào)文(CONNECT)之后,并接受到了服務(wù)端的連接報(bào)文確定(CONNACK)之后,才可以有進(jìn)行發(fā)布相關(guān)信息??墒荕QTT文檔指出客戶端在發(fā)送連接報(bào)文(CONNECT)之后就可以進(jìn)行發(fā)布了。而不需要等待連接報(bào)文確定(CONNACK)。如果服務(wù)端因?yàn)榭蛻舳瞬缓线m,可以完全不需要去處理和反應(yīng)之前送來(lái)的消息。 連接報(bào)文確定(CONNACK)的特點(diǎn)就是沒有有效載荷的部分??蛻舳讼胍雷约河袥]有連接成功服務(wù)端。就必須去查看可變報(bào)頭里面的回返碼。在CONNACK報(bào)文的可變報(bào)頭里面還有一叫Session Present。他用于表示服務(wù)端沒有保存會(huì)話狀態(tài)。這個(gè)筆者就不多講。各位自己看下圖吧。 注意:紅線為Session Present,綠線為返回碼。
|
|
來(lái)自: WindySky > 《基礎(chǔ)知識(shí)》