學習MQTT協議。若是隻是看了相關文檔就認爲能夠了。那是一個錯誤的觀念。筆者爲了能更好的去理解MQTT協議。看了很多相關的開源Broker的項目。惋惜這些項目通常都是不徹底的。不過從這些項目中筆者至少發現他們大部都是經過Netty這個通訊框架來完成的。哪怕是大型項目ActiveMQ也脫不了俗。特別是商用HiveMQ更是列爲重要的一部分。因此筆者接下來會用Netty框架來實現一些代碼。這樣子有助於咱們去理解MQTT協議。html
本節筆者會來說鏈接報文(CONNECT)。能夠說他是全部報文的基礎。全部的動做都必須在鏈接之上操做。咱們都知道MQTT是基於TCP/IP網絡協議的。並以字節流傳輸的。他的行爲動做更爲簡單。以下web
咱們能夠知道鏈接會用到倆個報文類型類——CONNECT報文和CONNACK報文。其中CONNECT報文比較複雜一點。能夠說是全部報文中信息種類最多的。CONNACK報文的最大特色就是沒有有效載荷部分。網絡
接下筆者就會講解一下鏈接的相關行爲。同時也但願讀者們記住筆者這裏講的通常是MQTT 3.1 和MQTT 3.1.1的協議。框架
CONNECT報文就是至關鏈接請求同樣子。因此當客戶端和服務端創建以後,服務端接受的第一份報文就必須是鏈接報文。相信這個不用筆者說明也知道爲何。同時客戶端這邊要保證鏈接報文只能發送一次。同時在服務端也要有驗證來自客戶端的鏈接報文只有一次。若是發送倆次以上的鏈接報文的話,很差意思請看成違反了協議斷開當前鏈接。從前面的圖片裏面咱們就知道若是客戶端發送一個鏈接報文(CONNECT)以後,服務端就會返回一個鏈接肯定報文(CONNACK)。若是客戶端在一段時間以後,尚未接收到來自服務端的鏈接肯定報文(CONNACK)的話,客戶端必定要斷開鏈接。同時要重起一個新的網絡鏈接。在發一次鏈接報文(CONNECT)。學習
咱們都知道控制報文分爲固定報頭+可變報頭+有效載荷部分。其中可變報頭和有效載荷並非必需要擁有的。而CONNECT報文倒是全部中筆者認爲最爲複雜的報文。不論是可變報頭仍是有效載荷部分他都擁有。spa
固定報頭htm
固定報頭的結構上一單已講過了。佔八個字節。從上一單的報文類型列表咱們知道他的值爲1。報文類型佔四個字節。因此他的二進制就是0001。在MQTT 3.1裏面有講到DUP、QoS、 RETAIN 都沒有被用到。這些加起來佔四個字節。這樣子的話筆者覺得固定報頭最終的二進制是0001。可是在MQTT 3.1.1裏面卻又說到以0保留了。因此最終二進制是00010000。簡單的講DUP、QoS、 RETAIN雖然沒有被用到,因此設置爲0。blog
可變報頭圖片
鏈接報文(CONNECT)的可變報頭的結果構分爲四個部分:協議名(Protocol Name)、協議等級(Protocol Level)、鏈接標誌(Connect Flags)、保持鏈接(Keep Alive)。其實協議名(Protocol Name)和協議等級(Protocol Level)筆者也不是很明白有什麼用。只是知道MQTT 3.1 的協議名是MQIsdp,協議等級是3。而MQTT 3.1.1的協議名倒是MQTT,等級是4。好吧。至少說明不一樣版本的MQTT協議存在不一樣的協議名和協議等級。這裏面在文檔也有一個動做存在。就是若是服務端發現協議名不正確的話,就必須斷開鏈接。若是是協議等級不對話,就是必須回返一個碼。開發
要問可變報頭裏面最重要的部分是哪一部分的話,筆者認爲是鏈接標誌(Connect Flags)。鏈接標誌裏面包括用戶名標誌(User Name Flag)、密碼標誌(Password Flag)、遺囑保留(Will Retain)、清理會話(Clean Session)、保留(Reserved)等。他們的位置如圖下。
清理會話(CleanSession)
客戶端和服務端之間的通訊時間,筆者認爲是一次會話。也就是在鏈接成功的時候爲會話開始。當斷開鏈接的時候表示一次會話結束了。即然是會話,天然就有會話狀態。這些狀態當前就是指通訊之間的信息。以下
1、服務端的會話狀態:
1)客戶端的訂閱信息。
2)已經發送給客戶端,可是尚未完成確認的QoS 1和QoS 2級別的消息。
3)即將傳輸給客戶端的QoS 1和QoS 2級別的消息
4)已從客戶端接收,可是尚未完成確認的QoS 2級別的消息
5)準備發送給客戶端的QoS 0級別的消息(可選)
2、客戶端的會話狀態:
1)已經發送給服務端,可是尚未完成確認的QoS 1和QoS 2級別的消息
2)已從服務端接收,可是尚未完成確認的QoS 2級別的消息
知道了會話狀態,就能夠明白清理會話就用來表示是否清除或是保存會話狀態。若是清理會話爲0的話,表示要保存這裏會話狀態。若是鏈接斷開了,在次鏈接的時候,服務要以什麼來得到已存在的會話狀態呢?客戶ID。這是在有效載荷分部分的下面會講到。根據客戶ID來找查有沒有相關的會話狀態存在。若是存在,就必須以存在的會話狀態來創建鏈接。若是沒有就建立一個新會話狀態。固然斷開以後,不論是客戶端仍是服務端都要保存當前會話狀態。這個舉動筆者認爲是爲了數據重用吧。也能夠說是保證數據不會丟失。若是清理會話爲1的話,事情就變的很簡單。給我清除掉就是行了。沒有一次鏈接都是一個新的會話。
遺囑標誌(Will Flag)
關看到遺囑倆個字就應該明白。用於表示在網絡鏈接突關閉的時候,要不要發送遺囑。遺囑標誌可關係遺囑QoS(Will QoS),遺囑保留(Will Retain),還有有效載荷裏面的遺囑主題(Will Topic)和遺囑消息(Will Message)。能夠說遺囑標誌是遺囑功能總開關。只當遺囑標誌爲1的時候。說明要用到遺囑功能。那遺囑QoS(Will QoS),遺囑保留(Will Retain)就能夠被啓用。也就是說遺囑主題(Will Topic)和遺囑消息(Will Message)必需要在有效載荷部分裏面出現。
遺囑QoS(Will QoS)
他的值主要要看遺囑標誌(Will Flag)是的值。若是遺囑標誌(Will Flag)是1的話,遺囑QoS能夠是0,1,2。這些值表示跟服務質量是同樣子。若是遺囑標誌(Will Flag)是0的話,那麼遺囑QoS必須也是0;
遺囑保留(Will Retain)
他的值也是要看遺囑標誌(Will Flag)是的值。若是遺囑標誌(Will Flag)是1的話,遺囑保留能夠是0或1。用於表示有沒有作於保留消息發佈;
用戶名標誌 User Name Flag 和 密碼標誌 Password Flag
筆者爲何把這倆個放在一塊兒呢?相信作過不少業務開發的人都知道用戶名和密碼。上面這倆個就是用於標示在有效載荷裏有沒有相關的部分。好比。當就用戶名標示爲1的時候,那就是說明有效載荷部分裏面有用戶名的信息。同理密碼標示爲1 就是表示有效載荷部分有密碼的信息。這裏有一點要注意就是若是用戶名標誌爲0的話,那密碼標誌就必須爲0。有用戶名和密碼的話,咱們就能夠在服務端作一些身分驗證的業務。同時還能夠加入一些權限。
上面筆者講過可變報頭分爲四個部分。剩下一個保持鏈接(Keep Alive)。保持鏈接(Keep Alive)表示在客戶端一個報文發送結束以後到下一次報文發送以前的空閒時間。單位爲秒。記住是在客戶端而不是服務端。若是在保持鏈接的時間內沒有發送任何報文的話。文檔裏面是要求發送一個PINGREQ報文。經過他判斷服務端和客戶端之間的鏈接狀態。若是在一時間段接受不到沒有收到服務端發來PINGRESP報文,那麼就應該關閉於服務端的鏈接。前面講都是在客戶端這邊要作的事情。服務端這固然也不能少。若是服務端判斷保持鏈接不爲0的時候,就要以保持鏈接值的1.5陪時間來判斷是否有接到報文。若是沒有接受報文的話,就要關閉跟客戶端之間的鏈接。值得注意的事。服務端要斷開客戶端不是根據保持鏈接來處理。而是查看這個客戶端是否是閒了。若是是的話,何時均可以斷開鏈接的。
有效載荷
有效載荷事實上就是通訊裏面的用戶要存放的信息。只是這裏面又不能全是用戶信息。還包括了一些MQTT須要的信息。這跟可變報頭的一些標誌有關係。可是無論什麼樣子。客戶ID是必須在第一位的。後面就是遺囑主題和遺囑消息。最後纔是用戶和密碼。
有效載荷 = 客戶ID + 遺囑主題 + 遺囑消息 + 用戶 + 密碼
爲了方便學習,筆者用軟件把包搞下來。上一章也講到過什麼樣子搞下。以下圖下
上面的圖片算是比較全的鏈接報文。筆者也根據相應的標示出他們所在的位置。圖片下方是報文相應的二進制流。紅線標出的數據是報文真時的數據對應。而綠色線表示是接下來紅線相關數據的長度。相信若是你看過MQTT相關的文檔就應該知道MSB和LSB關鍵字的做用。那麼圖片1紅線就是固定報頭,2紅線就是可變報頭。從3紅線開始後面都是有效載荷部分。
通上面咱們大至能瞭解MQTT的鏈接過程要作些什麼。在MQTT文檔裏面有一點筆者有一點吃驚。筆者覺得若是一個客戶端發送鏈接報文(CONNECT)以後,並接受到了服務端的鏈接報文肯定(CONNACK)以後,才能夠有進行發佈相關信息。但是MQTT文檔指出客戶端在發送鏈接報文(CONNECT)以後就能夠進行發佈了。而不須要等待鏈接報文肯定(CONNACK)。若是服務端由於客戶端不合適,能夠徹底不須要去處理和反應以前送來的消息。
鏈接報文肯定(CONNACK)的特色就是沒有有效載荷的部分。客戶端想要知道本身有沒有鏈接成功服務端。就必須去查看可變報頭裏面的回返碼。在CONNACK報文的可變報頭裏面還有一叫Session Present。他用於表示服務端沒有保存會話狀態。這個筆者就很少講。各位本身看下圖吧。
注意:紅線爲Session Present,綠線爲返回碼。