什么是mqtt?
????????與HTTP 協議一樣, MQTT 協議也是應用層協議,工作在 TCP/IP 四層模型中的最上層(應用層),構建于 TCP/IP協議上。 MQTT 最大優點在于,可以以極少的代碼和有限的帶寬,為連接遠程設備提供實時可靠的消息服務。作為一種低開銷、低帶寬占用的即時通訊協議,使其在物聯網、小型設備、移動應用等方面有較廣泛的應用
????????MQTT 協議是為工作在低帶寬、不可靠網絡的遠程傳感器和控制設備之間的通訊而設計的協議,它具有以下主要的幾項特性:
①、 使用發布/訂閱消息模式,提供一對多的消息發布,解除應用程序耦合。
②、基于 TCP/IP 提供網絡連接。
????????主流的 MQTT 是基于 TCP 連接進行數據推送的,但是同樣也有基于 UDP 的版本,叫做 MQTT-SN。這兩種版本由于基于不同的連接方式,優缺點自然也就各有不同了。
③、支持 QoS 服務質量等級。
根據消息的重要性不同設置不同的服務質量等級。
④、 小型傳輸, 開銷很小,協議交換最小化,以降低網絡流量。
????????這就是為什么在介紹里說它非常適合"在物聯網領域,傳感器與服務器的通信,信息的收集",要知道嵌入式設備的運算能力和帶寬都相對薄弱,使用這種協議來傳遞消息再適合不過了, 在手機移動應用方面, MQTT 是一種不錯的 Android 消息推送方案。
⑤、使用 will 遺囑機制來通知客戶端異常斷線。
⑥、基于主題發布/訂閱消息,對負載內容屏蔽的消息傳輸。
⑦、支持心跳機制。
MQTT 通信基本原理
服務端和客戶端
????????MQTT 是一種基于客戶端-服務端架構的消息傳輸協議,所以在 MQTT 協議通信中,有兩個最為重要的角色,它們便是服務端和客戶端
MQTT 主題
????????客戶端想要從服務器獲取信息,首先需要訂閱信息,"主題”在 MQTT 通信中是一個非常重要的概念,客戶端發布信息以及訂閱信息都是圍繞“主題”來進行的。
????????客戶端發布消息時需要為消息指定一個“主題”, 表示將消息發布到該主題;而對于訂閱消息的客戶端來說,可通過訂閱“主題” 來訂閱消息,這樣當其它客戶端或自己(當前客戶端)向該主題發布消息時, MQTT 服務端就會將該主題的信息發送給該主題的訂閱者(客戶端)。
????????在以上圖示中一共有三個 MQTT 客戶端, 它們分別是開發板、 手機和電腦。 MQTT 服務端在管理 MQTT通信時使用了“主題”來對信息進行管理。比如上圖所示,假設我們需要利用手機和電腦獲取開發板在運行過程中 SoC 芯片的溫度,那么首先電腦和手機這兩個客戶端需要向 MQTT 服務器訂閱主題“芯片溫度” ;接下來,當開發板客戶端向服務端的“芯片溫度”主題發布信息(假設信息的內容就是當前的溫度值) 后服務端就會首先檢查都有哪些客戶端訂閱了“芯片溫度”這一主題的信息,而當它發現訂閱了該主題的客戶端有一個手機和一個電腦,于是服務端就會將剛剛收到的“芯片溫度”信息轉發給訂閱了該主題的手機和電腦客戶端。
????????以上實例中, 開發板是“芯片溫度”主題的發布者,而手機和電腦則是該主題的訂閱者。
值得注意的是, MQTT 客戶端在通信時,角色往往不是單一的, 一個客戶端既可以作為信息發布者也可以同時作為信息訂閱者。如下圖所示:
????????圖中的所有客戶端都是圍繞“LED 控制”這一主題進行通信。此時,對于“LED 控制”這一主題來說,手機和電腦客戶端成為了 MQTT 信息的發布者而開發板則成為了 MQTT 信息的訂閱者(接收者)。
MQTT 發布/訂閱特性
?
客戶端相互獨立
????????MQTT 客戶端是一個個獨立的個體, 它們無需了解彼此的存在,依然可以實現信息交流。
空間上分離
????????MQTT 客戶端以及 MQTT 服務端它們在通信時是處于同一個通信網絡中的, 這個網絡可以是互聯網或者局域網; 只要客戶端聯網,無論他們遠在天邊還是近在眼前,都可以實現彼此間的通訊交流;其實網絡通信本就是如此,所以并不是 MQTT 通信所特有的。
時間上可異步
????????MQTT 客戶端在發送和接收信息時無需同步。這一特點對物聯網設備尤為重要
連接 MQTT 服務端
????????MQTT 客戶端連接服務端總共包含了兩個步驟:
????????①、首先客戶端需要向服務端發送連接請求,這個連接請求實際上就是向服務端發送一個 CONNECT報文,也就是發送了一個 CONNECT 數據包。
????????②、 MQTT 服務端收到連接請求后,會向客戶端發送連接確認。 連接確認實際上是向客戶端發送一個CONNACK 報文,也就是 CONNACK 數據包。
????????總結一句話就是:客戶端先向服務端發送 CONNECT報文,服務端收到連接請求后,再向待連接的客戶端發送 CONNACK 報文
CONNECT 報文
?
????????如果此 CONNECT 報文的格式或內容不符合 MQTT 規范,則服務器會拒絕客戶端的連接請求。
????????CONNECT 報文包含的信息如下圖所示:
????????所謂報文就是一個數據包, MQTT 報文組成分為三個部分:固定頭(Fixed header)、可變頭(Variable header)以及有效載荷(Payload,消息體)。這里我們簡單地介紹一下:
????????固定頭(Fixed header) : 存在于所有 MQTT 報文中, 固定頭中有報文類型標識,可用于識別是哪種 MQTT 報文,譬如該報文是 CONNECT 報文還是 CONNACK 報文,亦或是其它類型報文。
????????可變頭(Variable header) : 存在于部分類型的 MQTT 報文中,報文的類型決定了可變頭是否存在及其具體的內容。
?????????消息體(Payload) : 存在于部分類型的 MQTT 報文中, payload 就是消息載體的意思。
????????clientId--客戶端 id
????????clientId 是 MQTT 客戶端的標識,也就是 MQTT 客戶端的名字,MQTT 服務端可通過 clientId 來區分不同的客戶端。
????????keepAlive--心跳時間間隔
????????有些客戶端并不經常發送消息給服務端, 對于這種客戶端, MQTT 協議使用了類似心跳檢測的方法來判斷客戶端是否在線。客戶端在沒有向服務端發送信息時(空閑時) ,可以定時向服務端發送一個心跳數據包,這個心跳包也被稱作心跳請求, 心跳請求的作用正是用于告知服務端,當前客戶端依然在線。
????????譬如 keepAlive=60,表示告訴服務端,客戶端將會每隔 60 秒左右向服務端發送心跳包。
????????cleanSession--清除會話
????????這是一個布爾值, cleanSession 標志可用于控制客戶端與服務端在連接和斷開連接時的行為,我們舉個例子來進行說明, QQ、微信這些聊天軟件大家都用過,假設當前你的 QQ 賬號沒有登錄或者說當前處于離線狀態,與服務器斷開了連接; 而在離線期間,你的 QQ 好友給你發了幾條信息; 由于當前你的 QQ 處于離線狀態,自然是接收不到好友發送過來的信息,但是, 當你的 QQ 恢復連接狀態時,立馬會接收到好友在離線期間所發給你的信息
????????如果連接服務端時 cleanSession=0, 當 MQTT 客戶端由離線(與服務端斷開連接)再次上線時,離線期間發給客戶端的所有 QoS>0 的消息仍然可以接收到;如果連接服務端時 cleanSession=1, 當 MQTT 客戶端由離線(與服務端斷開連接)再次上線時,離線期間發
給客戶端的所有消息一律接收不到。
????????說白了,想接收離線消息, 客戶端連接服務端時就必須使用 cleanSession=0;除了這個作用之外,如果cleanSession=0,則 MQTT 服務端會在客戶端斷開連接之后“記住” MQTT 客戶端在線期間所訂閱的所有“主題”;也就是說,服務端會保存、存儲客戶端所訂閱的主題。
????????如果 cleanSession=1,客戶端既無法接收到離線消息、服務端也不會記住該客戶端所訂閱的主題, 服務端不會保存客戶端的會話狀態, 每次連接都是一次新的會話
下面再看看 MQTT 服務端接收到客戶端發來的連接請求后所回復的 CONNACK 報文詳細內容
CONNACK 報文

returnCode--連接返回碼
????????當服務端收到了客戶端的連接請求后,會向客戶端發送 returnCode(連接返回碼), 用來說明連接情況。如果客戶端與服務端成功連接,則返回數字“0”。如果未能成功連接,返回碼將會是一個非零的數字,具體這個數字的含義,請見下表

???????sessionPresent
????????在 cleanSession=0 的情況下, 當客戶端連接到服務器之后, 可通過 CONNACK 報文中返回的sessionPresent 來查詢服務端是否為客戶端保存了會話狀態(客戶端上一次連接時的會話狀態信息) , 如果服務端已為客戶端保存了上一次連接時的會話狀態,則 sessionPresent=1,如果沒有保存會話狀態,則sessionPresent=0。
????????如果 cleanSession=1, 在這種情況下,客戶端是不需要服務端保存會話狀態的, 那么服務端發送的確認連接 CONNACK 報文中, sessionPresent 肯定是 false(sessionPresent=0) ,也就是說,服務端沒有保存客戶端的會話狀態信息。
????????簡言之, CONNACK 報文的 sessionPresent 與 CONNECT 報文的 cleanSession 相互配合。其作用是客戶端發送連接請求時,服務端告知客戶端有沒有保存會話狀態。這個被服務端保存的會話狀態是來自于上一次客戶端連接時,譬如離線消息以及上一次連接時客戶端所訂閱的主題。
斷開連接
?
????????當 MQTT 客戶端連接到服務端之后,在后續的通信過程中,如果客戶端想要斷開與服務端的連接,此時客戶端可以主動向服務端發送一個 DISCONNECT 報文來斷開與服務端的連接,如下圖所示
發布消息、訂閱主題與取消訂閱主題
?
PUBLISH– 發布消息
????????當客戶端連接到服務端之后,就可以向服務端發布消息了, 每條發布的消息必須指定一個“主題”,表示向某主題發布消息,MQTT 客戶端向服務端發布消息其實就是向服務端發送一個 PUBLISH 報文, 服務端收到客戶端發送過來的 PUBLISH 報文之后,會向發送發回復一個報
SUBSCRIBE--訂閱主題
????????客戶端要想訂閱主題,首先要向服務端發送主題訂閱請求。客戶端是通過向服務端發送 SUBSCRIBE 報文來實現這一請求的。該報文包含有一系列“訂閱主題名”。請留意,一個 SUBSCRIBE 報文可以包含有單個或者多個訂閱主題名。也就是說,一個 SUBSCRIBE 報文可以用于訂閱一個或者多個主題。
????????另外每一個 SUBSCRIBE 報文還包含有“報文標識符”。報文標識符可用于對 MQTT 報文進行標識。不同的 MQTT 報文所擁有的標識符不同。 MQTT 設備可以通過該標識符對 MQTT 報文進行甄別和管理。當客戶端向服務端發送 SUBSCRIBE 報文,服務端接收到 SUBSCRIBE 報文之后會向客戶端回復一個SUBACK 報文(訂閱確認報文),如下圖所示:
UNSUBSCRIBE--取消訂閱主題
?
????????客戶端訂閱了某一主題之后,可以隨時取消訂閱, MQTT 協議提供了這樣的操作。
客戶端通過向服務端發送一個 UNSUBSCRIBE 報文來取消訂閱主題,當服務端接收到 UNSUBSCRIBE報文后,會向發送發回復一個 UNSUBACK 報文(取消訂閱確認報文),如下圖所示: