A. MQTT 基本認知
物聯網 (internet of thing) ,表示的是可以把一些帶某些感測器的設備(終端),接入到互聯網的行為。
通過互聯網連接這些設備,這些設備就能夠互相協作。
而 MQTT 就是這些設備之間數據通信的一個基於 TCP/IP 的協議。
每個終端都和實現了 MQTT 協議的代理/伺服器相連。
通過 published MQTT 代理伺服器的某個 主題 發送數據。
通過 subscription 從 MQTT 代理伺服器獲取自己訂閱的 主題 數據。
MQTT 協議是一種輕量級的、靈活的網路協議。並且非常適合 IOT 的場景。
大多數開發人員已經熟悉了 HTTP WEB 協議。那麼為什麼不讓 IOT 設置鏈接到 WEB 服務?
設備可以採用 HTTP 請求的形式發送數據,並採用 HTTP 響應的形式從伺服器獲取數據,接受更新。
因為對於 IOT 的設備來說,這種 主動請求--> 被動等待應答的 數據傳輸模型存在嚴重的局限性:
那麼,MQTT 為什麼如此輕便且靈活?MQTT 協議的一個關鍵的特性是 發布/訂閱模型 。它將數據的發布者和接受者分離。
一個設備終端既可以是數據的發布者 (published) 也可以是數據的訂閱者 (subscription) 。
一個設備如果要發布數據,只需要往代理伺服器中 相應的主題發布數據內容即可。
一個設備如果需要接受到數據,只需要在代理伺服器中, 提前訂閱自己需要關注的主題即可。
MQTT 最基本的體驗,就是使用 mosquitto 。
Mosquitto是一款實現了 MQTT v3.1 協議的開源消息代理軟體,提供輕量級的,支持發布/訂閱的的消息推送模式,使設備對設備之間的短消息通信簡單易用。
它可以理解成一個 MQTT 的代理伺服器。
基本步驟如下:
安裝成功截圖
使用 brew services start mosquitto 啟動 MQTT 服務
運行截圖
然後再打開另外兩個終端窗口,模擬兩個IOT設備。A 訂閱 MQTT 服務。B 向 MQTT 的服務發送數據。
A訂閱當前MQTT的某個服務。
B向 MQTT 伺服器發布(published) 數據。
然後,我們就可以在A控制台里看到由 B 通過 MQTT 服務發送的數據了。
基本流程圖
控制台 A 向 MQTT 伺服器訂閱 dw/demo 服務,並被動的等待 MQTT 伺服器返回數據。
控制台 B 主動的向 MQTT 伺服器的 dw/demo 服務發送 published 數據,之後。伺服器會主動向事先訂閱了 dw/demo 的終端分發此消息。
MQTT 是一種鏈接協議,它指定了如何組織數據位元組並通過 TCP/IP 網路傳輸它們。但實際上,開發人員並不需要鏈接這個鏈接協議的具體細節。我們只需要知道,每條消息都有一個命令和數據有效負載。該命令定義消息類型(比如 CONNECT 消息或者 SUB SCRIBE 消息)。所有的 MQTT 庫和工具都提供了直接處理這些消息的基本方法,並且能自動填充一些必要的欄位(在數據包的對應位元組填充),比如消息和客戶端 ID。
首先客戶端發送一條 CONNECT消息 來鏈接代理。CONNECT 消息要求建立從客戶端到代理伺服器的鏈接。
CONNECT 命令的基本參數
當客戶端向代理伺服器發送一條 CONNECT 命令之後,伺服器會調用 CONNACK 命令,告知服務鏈接的狀態。
CONNACK 命令的基本參數
當客戶端和伺服器建立連接之後,客戶端就可以向伺服器訂閱某些主題的。(發送一條或多條 SUBSCRIBE消息 )。
表明當伺服器接受到其他終端推送的此主題數據時,伺服器會默認發送給它。
SUBSCRIBE 參數列表
當客戶端成功的向伺服器訂閱某個主題之後,伺服器會返回一條 SUBACK 的消息,其中包含一個或者多個 returnCode 參數。
SUBACK消息參數
returnCode : 值 0 - 2 ,表示成功訂閱,並返回這個訂閱消息的 QOS。值 128 : 訂閱失敗。
既然客戶端可以向伺服器訂閱某個主題,當然也可以取消訂閱。
與 SUBSCRIBE 訂閱命令相反的命令是 UNSUBSCRIBE 取消訂閱命令。
此命令非常簡單。只有一個topic(主題)參數。
上面講的是訂閱,訂閱是需要有消息從伺服器發送過來的。但是伺服器本身基本不產生數據,那數據從何而來呢?
通過另外一個客戶端執行 PUBLISH 命令,往代理伺服器發送數據。並最終通過代理伺服器將數據傳遞給訂閱了此服務的客戶端。
PUBLISH 消息參數
對於 MQTT 的一張基本理解圖
基本流程圖:
最後總結
參考資料: 初識 MQTT
B. mqtt協議的Android端介面該怎麼寫啊,有點亂額,mqtt協議不是沒有介面嘛只有一堆主題
一、MQTT協議
MQTT(Message Queuing Telemetry Transport,消息隊列遙測傳輸)是IBM開發的一個即時通訊協議,有可能成為物聯網的重要組成部分。該協議支持所有平台,幾乎可以把所有聯網物品和外部連接起來,被用來當做感測器和制動器(比如通過Twitter讓房屋聯網)的通信協議。
MQTT協議是為大量計算能力有限,且工作在低帶寬、不可靠的網路的遠程感測器和控制設備通訊而設計的協議,它具有以下主要的幾項特性:
1、使用發布/訂閱消息模式,提供一對多的消息發布,解除應用程序耦合;
2、對負載內容屏蔽的消息傳輸;
3、使用 TCP/IP 提供網路連接;
4、有三種消息發布服務質量:
MQTT協議通信過程
二、MQTT協議的數據格式
MqttMessage
private boolean mutable = true;
private byte[] payload;
private int qos = 1;
private boolean retained = false;
private boolean p = false;
三、MQTT協議在Android端的集成
在app的build.gradle中添加依賴。或者自己去網上下載對應版本的jar包也可以。
repositories {
maven {
url"https://repo.eclipse.org/content/repositories/paho-releases/"
}
}
dependencies {
compile 'org.eclipse.paho:org.eclipse.paho.client.mqttv3:1.1.0'
compile 'org.eclipse.paho:org.eclipse.paho.android.service:1.1.0'
}
C. MQTT 系列之 MQTT broker 的連接
client 在可以發布和訂閱消息之前,必須先連接到 broker,下面我們來看一下連接到 broker 的流程。
連接的建立由 Client 端發起,Client 端首先向 broker 發送一個 CONNECT 數據包,CONNECT 數據包包含以下內容(這里我們略過 fixed header)。
在 CONNECT 數據包可變頭中,含有以下信息。
協議名稱(Protocol name) :值固定為 MQTT 字元。
協議版本 :對於 MQTT3.1.1 來說,值為 4.
用戶名標識 :消息體中是否有用戶名欄位, 1bit , 0 或 1。
密碼標識 :消息體中是否有密碼欄位,1bit,0 或 1。
遺願消息 Retain 標志(will retain) :標識遺願消息是否是 retain 消息,1 bit,0 或 1。
遺願消息 QoS 標志 (will QoS) :標志遺願消息的 QoS 是否存在,1 bit,0 或 1。
會話清除標志(clean session) :標識 Client 是否建立一個持久化的會話,1bit,0或1,當 clean session 的標識為 0 時,代表 client 希望建立一個之久的會話連接,broker 間存儲該 client 訂閱的主題和未接受的消息,否則broker不會存儲這些數據,同時在建立連接時清除這個 client 之前的持久化會話保存的數據。
心跳保持(keep alive) :設置一個單位為秒的時間間隔,client 和 broker 之間在這個時間內至少需要進行一次消息交互,否則 client 和 broker 會認為它們之間的連接已斷開。
CONNECT 數據包的消息體中包含了以下數據。
客戶端標識符(client identifier) :Client Identifier 是用來標識 Client 身份的欄位,在 MQTT3.1.1 的版本中,這個欄位的長度是 1 到 23個位元組,而且只能包含數字和26個字母(包括大小寫),broker 通過這個欄位來區分不同的 client。所有在連接的時候,client應該保證它的 identifier 是唯一的,通常我們可以使用比如 UUID,唯一的設備硬體標識,或者 Android 設備的 DEVICE_ID 等作為 Client identifier 的取值來源。MQTT 協議中要求 Client 連接時必須帶上 Client Identifier,但是也允許 broker 在實現時 Client Identifier 為空,這時 Broker 會為 Client 分配一個內部唯一的 Identifier。如果你需要使用持久化會話,那就必須自己為 Client 設定一個唯一的 Identifier。
用戶名(Username) :如果可變頭中的用戶標識設為 1,那麼消息體中將包含用戶名欄位,Broker 可以使用用戶名和密碼來對接入的 Client 進行驗證,只允許已經授權的 Client 接入。注意不同的 Client 需要使用不同的 Client Identifier,但它們可以使用同樣的用戶名和密碼進行連接。
密碼(password) :如果可變頭中的密碼標識為1,那麼消息體中將包含密碼欄位。
遺願主題(will topic) :如果可變頭中遺願標識設為1,那麼消息體中將包遺願主題,當 Client 非正常地中斷連接的時候,Broker將向指定的遺願主題中發布遺願消息。
遺願消息(will message) :如果可變頭中的遺願標志為1,那麼消息體中將包含遺願消息,當 Client 非正常地中斷連接的時候,Broker 將向指定的遺願主題中發布由該欄位指定的內容。
當 broker 收到 client 的 CONNECT 數據包之後,將檢查並校驗 CONNECT 數據包的內容,之後回復 Client 一個 CONNACK 數據包。
CONNACK 數據包包含以下內容(這里略過 Fixed header)。
CONNACK 數據包的可變頭中,含有如下信息:
會話存在標識(Session Present Flag) :用於標識在 Broker 上,是否存在該 client(用 client identifier 區分)的持久性會話,1bit,0或1。當 Client 在連接時設置 clean session = 1 ,則 CONNACK 中的 Session Present Flag 始終為 0;當 client 在連接時設置 clean session = 0 時,那麼分為兩種情況:如果 broker 上面保存了這個 Client 之前留下的持久性會話,那麼 CONNACK 中的 session present flag 值為 1,如果 broker 沒有保存該 client 的任何會話數據,那麼 CONNACK 中 session present flag 值為 0.
連接返回碼: 用於標識 client 是否連接建立成功,連接返回碼如下:
在這里強調一下 code 4 和 5。Return Code 4 在 MQTT 協議中的含義是 Username 和 Password 的格式不正確,但是在大部分的 Broker 實現中,在使用錯誤的用戶名密碼時,得到的返回碼也是 4。所以這里我們認為 4 就是代表錯誤的用戶名或密碼。Return Code 5 一般在 Broker 不使用用戶名和密碼而使用 IP 地址或者 Client Identifier 進行驗證的時候使用,來標識 Client 沒有通過驗證。
CONNACK 沒有 payload。
Client 主動關閉連接的流程很簡單,只需要向 broker 發送一個 DISCONNECT 數據包就可以了。DISCONNECT 數據包沒有可變頭和消息體。在 Client 發送完 DISCONNECT 後,無需等待 broker 的回復(broker 也不會有回復),直接關閉底層的 tcp 連接即可。
MQTT 協議規定 Broker 在沒有收到 Client 的 DISCONNECT 數據包之前都應該保持和 Client 連接,只有 Broker 在 Keep Alive 的時間間隔里,沒有收到 Client 的任何 MQTT 數據包的時候會主動關閉連接。一些 Broker 的實現在 MQTT 協議上做了一些拓展,支持 Client 的連接管理,可以主動地斷開和某個 Client 的連接。
Broker 主動關閉連接之前不會向 Client 發送任何 MQTT 數據包,直接關閉底層的 TCP 連接就完事了。
D. 消息推送是如何實現的
消息推送是當伺服器有新消息需推送給用戶時,先發送給應用App,應用App再發送給用戶。以下是幾 種主流的消息推送方式:E. 【內部分享】MQTT協議解讀及使用經驗
時間:2018-07-26
Q: 什麼是網路連接?
A: 網路連接是傳輸層定義的概念,在傳輸層以下只存在網路數據包的相互交換。
所謂連接,其實也不是在網路上有一條真實存在的數據通道。只要通信雙方在一段時間內持續保持數據包交換,就可以視為雙方建立的連接並沒有斷開。
連接的建立是依託於TCP協議的三次握手,一旦連接已經建立完畢,通信雙方就可以復用這條虛擬通道進行數據交換。如果連接保持長時間工作一直沒有被中斷,那麼這樣的TCP連接就俗稱為長連接。
Message Queue Telemetry Transport ,中文直譯: 消息隊列遙測傳輸協議 。
在MQTT協議被設計出來的年代,還沒有物聯網這么時髦的詞彙,當年叫做 遙測設備 。
MQTT協議真正開始聲名鵲起的原因,是其正好恰恰踩中移動互聯網發展的節拍,為消息推送場景提供了一個既簡便又具有良好擴展性的現成解決方案。
http://docs.oasis-open.org/mqtt/mqtt/v3.1.1/os/mqtt-v3.1.1-os.html
可以看出,MQTT對消息頭的規定十分精簡, 固定頭部佔用空間大小僅為1個位元組 ,一個最小的報文佔用的空間也 只有兩個位元組 (帶一位元組的長度標識位)。
這也是MQTT協議針對不穩定及帶寬低下的網路環境做出的特定設計 - - - - 盡可能地節省一切不必要的網路開銷 。
Q:為什麼MQTT協議需要心跳報文(PINGREQ, PINGRESP)來維護連接狀態,只監控該TCP的連接狀態是否可以實現目的?
A: TCP數據傳輸默認的超時時間過長,不符合應用層上細粒度的要求。
TCP數據傳輸超時的情況可分成三種: 服務端斷開 、 客戶端斷開 、 中間網路斷開 。
在前兩種場景下,若斷開操作是一方主動發起的,即表示為TCP連接正常結束,雙方走四次揮手流程;若程序異常結束,則會觸發被動斷開事件,通信另一方也能立刻感知到本次連接所打開的 Socket 出現中斷異常。
唯獨中間網路的狀態是通信雙方不能掌握的。 在Linux系統下 ,TCP的連接超時由內核參數來控制,如果通信中的一方沒有得到及時回復,默認會主動再嘗試 6次 。如果還沒有得到及時回應,那麼其才會認定本次數據超時。
連帶首次發包與六次重試,Linux系統下這7次發包的超時時間分別為 2的0次方 至 2的6次方 ,即1秒、2秒、4秒、8秒、16秒、32秒、64秒,一共127秒。MQTT協議認為如此長的超時時間對應用層而言粒度太大,因此其在應用層上還單獨設計屬於自身的心跳響應控制。常見的MQTT連接超時多被設定為 60秒 。
擴展知識 - TCP的KeepAlive機制: http://hengyunabc.github.io/why-we-need-heartbeat/
由通信中的 報文標識符 ( Packet Identifier )傳達。
Q:僅Publish與Pubrec能保證消息只被投遞一次嗎?
A: 業務上可以實現,但MQTT協議並沒有如此設計,原因如下:
每個消息都會擁有屬於自己的報文標識符,但如果需要兩次數據交換就實現消息僅只收到一次,就需要通信雙方記錄下每次使用的報文標識符,並且在處理每一條消息時都需要去重處理,以防消息被重復消費。
但MQTT協議最初被設計的工作對象是輕量級物聯設備,為此在協議的設計中報文標識符被約定為 可重用 ,以減少對設備性能的消耗,換回的代價不得不使用四次網路數據交換,才能確保消息正好被消費一次。
Q:兩個不同客戶端在發布與訂閱同一Topic下的消息時,都可以提出通信Qos要求,此時以哪項為基準?
A: 偽命題,故意在分享時埋下坑,等人來踩。
兩個不同客戶端的通信是需要 Broker 進行中轉,而不是直連。因此,通信中存在兩個不同的會話,雙方的Qos要求僅僅作用於它們與 Broker 之間的會話,最終的Qos基準只會向最低要求方看齊。
例:遺囑消息的正確使用方式可參考此篇文章: https://www.hivemq.com/blog/mqtt-essentials-part-9-last-will-and-testament
雖然可以藉助 Retain Message 實現綁定一條消息至某個Topic,以達到消息的暫時保留目的。
但首先 Retain Message 並不是為存儲場景而設計的,再次MQTT協議並沒有對消息的持久化作出規定,也就是說Broker重啟後,現有保留消息也將丟失。
Q:兩種特殊消息的使用場景?
A: 遺囑消息,多用於客戶端間獲取互相之間異常斷線的消息通知;
保留消息,可保存 最近一條 廣播通知,多用於公告欄信息的發布。
Eclipse Mosquitto :MQTT協議的最小集實現
有 EMQ , HiveMQ , RabbitMQ MQTT Adapter 等。
Qos=2 消息保障的網路I/O次數過多,如果不是必需,盡少在程序里使用此類消息。
畢竟當初其設計的目的是為了減少設備的性能佔用,但若應用場景並不是物聯網,而是用於手機、電腦或瀏覽器端等現在已不缺性能的設備上,最好在報文體中,使用UUID生成全局唯一的消息ID,然後自行在業務解析中判斷此報文是否被消費過。
或者,業務方在處理消息時保證其被消費的冪等性,也可消除重復消息對系統帶來的影響。
正如MQTT協議並沒有依賴TCP連接狀態,自己在應用層協議上實現心跳報文來控制連接狀態,業務方作為MQTT協議的使用者,也不要完全依賴協議的工作狀態,而是依託MQTT協議建立屬於業務本身的信息匯報機制,以加強系統的穩健性。
Retain Message 可視為客戶端主動拉取的行為。如果業務系統採用 HTTP+MQTT 雙協議描述業務過程,主動拉取的操作也可使用 HTTP 請求替代。
作為一個長連接型的應用,上線前需要根據業務量級,評估對操作系統 埠數 與 文件描述符 的佔用要求,以防伺服器資源被打滿。
在服務端的配置文件和客戶端的連接參數中,都擁有 max_inflight_messages 此項配置,來維護 Qos=1 or 2 消息是否被成功消費的狀態。
MQTT 最初被設計為物聯網級的通信協議,因此此參數的默認配額較小(大多數情況下被限制到10至20)。
但如果將MQTT協議應用至手機、PC或Web端的推送場景時,硬體性能已不在是瓶頸,在實際使用中推薦把此參數調大。
Mosquitto提供Bridge功能,需要我們自己配置。
Bridge 意為橋接,當我們把兩台Broker橋接在一起時,只需要修改一台Broker的配置,填上另一台Broker的運行地址。前一台Broker將作為客戶端發布與訂閱後一台Broker的所有Topic,實現消息互通的目的。
橋接帶來的問題有以下幾點:
我的建議:
Websockets協議被設計的目的是為瀏覽器提供一個全雙工的通信協議,方便實現消息推送功能。
在Websockets協議被設計出來前,受限於HTTP協議的一問一答模型,消息的推送只能靠輪詢來實現,在資源消耗與時效性保障上,均難以達到令人滿意的效果。
Websockets協議復用了HTTP協議的頭部信息,告知瀏覽器接下來的操作將觸發協議升級,然後通信雙方繼續復用HTTP的Header,但報文內容已轉變為雙方均接受的新協議的格式。
Websockets協議改進了網頁瀏覽中的消息推送的方式,因此被廣泛應用在聊天、支付通知等實時性要求比較高的場合下。
MQTT協議重點在於 消息隊列的實現,其對消息投遞的方式作出約定,並提供一些額外的通信保障 。
MQTT可採取原生的TCP實現,也有基於Websockets的實現版本。當然後者在網路位元組的利用率上,不如前者那麼精簡。但瀏覽器端無法直接使用TCP協議,所以就只能基於Websockets協議開發。
不過基於Websockets的應用也有方便之處:一是證書不需要額外配置,直接與網站共用一套基礎設施;二是可使用 Nginx 等工具管理流量,與普通HTTP流量可共用一套配置方法。
MQTT非常適合入門,原因如下:
實際的應用場景遠比理想中的復雜,無法一招走遍天下,必須做好取捨。
MQTT協議在這方面做得很優秀,以後工作中可以作為參考,設計好自己負責的業務系統。
F. 關於mosquitto怎麼用c語言實現消息的訂閱和發送(mqtt)
1.目標:測試Mosquitto使用MQTT協議發消息的相關性能指標,包含發送速度,並發負載能力,資源佔用,消息到達率。
2.MQTT協議簡介:
1).建立長連接。客戶端發起請求和服務端建立長連接,建立成功後,服務端會返回ACK(CONNACK)
2).客戶端訂閱:客戶端發起訂閱,訂閱成功後,服務端會返回ACK(SUBACK)
3).發消息:發布者會給服務端發消息,服務端在把消息給合適的客戶端。
Qos=0(服務質量):客戶端消息收到後,不會發出ACK給服務端(PUBACK)。
Qos =1:服務端會發ACK給發布者,客戶端收到消息後會發ACK給服務端。
4).取消訂閱:客戶端發起取消訂閱,服務端返回ACK(UNSUBACK)
5)Pingreq&Pingresp:客戶端和服務端會保持心跳。
3.存在問題:
1. 如何模擬出40W的用戶
2. 如何長連接,訂閱,發消息,取消訂閱,Pingreq行為如何實現。
4. python開源庫 Mosquitto.py,解決所有問題
1. 模擬40W用戶
a)可以使用虛擬機和Mosquitto.py實現,具體為:一般一台虛擬機最多是6W+的模擬數據(需要修改句柄數,我使用5W),方法是Client_id可以簡單的做出5W個來,然後調用Mosquitto裡面的connect方法,建立長連接。准備8台虛擬機就可以實現40W客戶端的模擬
2.行為的模擬
a)訂閱:Mosquitto.subscribe / 發消息:Mosquitto.publish / 取消訂閱:Mosquitto.unsubscribe。 簡單一句話 Mosquitto庫實現所有的行為.
5. 指標:發送速度,到達率,並發負載,資源佔用。
a. 發送速度:服務端日誌記錄,分析解決
b. 到達率: 1.客戶端記錄下收到消息,分析計算。2.計算服務端收到的PUBACK(客戶端收到消息返回的ACK),進行計算
c. 並發負載:5W 用戶不斷增加,注意觀察服務端的負載情況。
e.資源佔用:其實主要是cpu/mem/帶寬:cpu多核的話需要觀察top命令下的_id欄位, mem可以觀察free -h命令的剩餘mem, 帶寬可以使用mpstat進行觀察
6. 可以遇見問題:
a. 模擬客戶端的虛擬機需要修改句柄數才能支持5W的客戶端模擬數量
b. 要先吃透MQTT協議的流程,吃透了進行測試會非常的方便
c. Clear session,設置為true則不為客戶端保留休息,設置為false保留消息。其實就是客戶端離線後在連接上可以收到之前推出的消息。
G. 如何採用mqtt協議實現android消息推送
使用一個代理伺服器message broker,客戶端client連接上這個伺服器,然後告訴伺服器,可以接收哪些類型的消息,同時client也可以發布自己的消息,這些消息根據協議的內容,可以別的client獲取。這樣就實現了消息推送。H. 3. MQTT簡要介紹
——
[1.MQTT項目工程](https://github.com/LiamBindle/MQTT-C)
[2.MQTT API說明文檔](https://liambindle.ca/MQTT-C/group__api.html)
[3.MQTT協議中文版](https://mcxiaoke.gitbooks.io/mqtt-cn/content/mqtt/01-Introction.html)
MQTT是一個客戶端服務端架構的發布/訂閱模式的消息傳輸協議。它的設計思想是輕巧、開放、簡單、規范,易於實現。這些特點使得它對很多場景來說都是很好的選擇,特別是對於受限的環境如機器與機器的通信(M2M)以及物聯網環境(IoT)。
MQTT協議通過交換預定義的MQTT控制報文來通信。
報文格式: 固定包頭+可變包頭+payload。
固定包頭: 由兩個位元組組成
byte1 高四位表示控制報文的類型,低四位表示控制報文類型的標志位。
byte2表示剩餘長度,包括可變報頭和負載的數據。剩餘長度不包括用於編碼剩餘長度欄位本身的位元組數。
可變包頭:
某些MQTT控制報文包含一個可變報頭部分。它在固定報頭和負載之間。可變報頭的內容根據報文類型的不同而不同。可變報頭的報文標(Packet Identifier)欄位存在於在多個類型的報文里。
有效載荷:
對於PUBLISH來說有效載荷就是應用消息。
——1. 網路建立連接後,客戶端發送的第一個報文必須是CONNECT報文
——2. 客戶端只能發送一次CONNECT 報文,發送兩次會當作協議違規處理,並斷開連接。
——3. 有效載荷包括:客戶端唯一標識符,will主題,will消息,用戶名和密碼。
——4.可變包頭:協議名(protocol name)+協議級別(protocol level)+連接標志(connect flags)+保持連接(keep alive)。
——5. 協議名: MQTT的UTF-8編碼的字元串。(MSB+LSB +MQTT 六個位元組)
——6. 協議級別:一個位元組
——7. 連接標志:一個位元組,包含一些用於指定MQTT連接行為的參數。同時還指出有效載荷中的欄位是否存在。服務端必須驗證CONNECT控制報文的保留標志位(第0位)是否為0,如果不為0必須斷開客戶端連接。
——8. 清理會話:byte8 的bit1位標志。
這個二進制位指定了會話狀態的處理方式。客戶端和服務端可以保存會話狀態,以支持跨網路連接的可靠消息傳輸。這個標志位用於控
制會話狀態的生存時間。
標志設置為0:服務端必須基於當前會話(使用客戶端標識符識別)的狀態恢復與客戶端的通信。
標志設置為1:客戶端和服務端必須丟棄之前的任何會話並開始一個新的會話。會話僅持續和網路連接同樣長的時間。
——9. 遺囑標志 WILL FLAG: byte8的bit2位標志。
遺囑標志(Will Flag)被設置為1,表示如果連接請求被接受了,遺囑(Will Message)消息必須被存儲在服務端並且與這個網路連接關聯。之後網路連接關閉時,服務端必須發布這個遺囑消息,除非服務端收到DISCONNECT報文時刪除了這個遺囑消息。
服務端發送CONNACK報文響應從客戶端收到的CONNECT報文。服務端發送給客戶端的第一個報文必須是CONNACK。
如果客戶端在合理的時間內沒有收到服務端的CONNACK報文,客戶端應該關閉網路連接。合理 的時間取決於應用的類型和通信基礎設施。
CONNACK報文沒有有效載荷。
PUBLISH控制報文是指從客戶端向服務端或者服務端向客戶端傳輸一個應用消息。
固定包頭:
注意byte1的bit3為重發標志DUP。
如果DUP標志被設置為0,表示這是客戶端或服務端第一次請求發送這個PUBLISH報文。如果DUP標志被設置為1,表示這可能是一個早前報文請求的重發。
服務端發送PUBLISH報文給訂閱者時,收到(入站)的PUBLISH報文的DUP標志的值不會被傳播。發送(出站)的PUBLISH報文與收到(入站)的PUBLISH報文中的DUP標志是獨立設置的,它的值必須單獨的根據發送(出站)的PUBLISH報文是否是一個重發來確定。
可變包頭:
主題名 :topic name
報文標識符 :packet identitfier。
有效載荷 :有效載荷包含將被發布的應用消息。數據的內容和格式是應用特定的。有效載荷的長度這樣計算:用固定報頭中的剩餘長度欄位的值減去可變報頭的長度。包含零長度有效載荷的PUBLISH報文是合法的。
響應:PUBLISH報文的接收者必須按照根據PUBLISH報文中的QoS等級發送響應。
PUBACK報文是對QoS 1等級的PUBLISH報文的響應。
PUBACK報文沒有有效載荷。
PUBREC報文是對QoS等級2的PUBLISH報文的響應。它是QoS 2等級協議交換的第二個報文。
PUBREC報文沒有有效載荷。
PUBREL報文是對PUBREC報文的響應。它是QoS 2等級協議交換的第三個報文。
PUBREL報文沒有有效載荷。
PUBCOMP報文是對PUBREL報文的響應。它是QoS 2等級協議交換的第四個也是最後一個報文。
PUBCOMP報文沒有有效載荷。
客戶端向服務端發送 SUBSCRIBE 報文用於創建一個或多個訂閱。每個訂閱注冊客戶端關心的一個或多個主題。為了將應用消息轉發給與那些訂閱匹配的主題,服務端發送PUBLISH報文給客戶端。SUBSCRIBE報文也(為每個訂閱)指定了最大的QoS等級,服務端根據這個發送應用消息給客戶端。
有效載荷:
SUBSCRIBE報文的有效載荷包含了一個主題過濾器列表,它們表示客戶端想要訂閱的主題。SUBSCRIBE報文的有效載荷必須包含至少一對主題過濾器 和 QoS等級欄位組合。沒有有效載荷的SUBSCRIBE報文是違反協議的。
響應:
服務端收到客戶端發送的一個SUBSCRIBE報文時,必須使用SUBACK報文響應,SUBACK報文必須和等待確認的SUBSCRIBE報文有相同的報文標識符。
服務端發送SUBACK報文給客戶端,用於確認它已收到並且正在處理SUBSCRIBE報文。SUBACK報文包含一個返回碼清單,它們指定了SUBSCRIBE請求的每個訂閱被授予的最大QoS等級。
有效載荷:
有效載荷包含一個返回碼清單。每個返回碼對應等待確認的SUBSCRIBE報文中的一個主題過濾器。返回碼的順序必須和SUBSCRIBE報文中主題過濾器的順序相同。
客戶端發送UNSUBSCRIBE報文給服務端,用於取消訂閱主題。
有效載荷 :
UNSUBSCRIBE報文的有效載荷包含客戶端想要取消訂閱的主題過濾器列表。
UNSUBSCRIBE報文中的主題過濾器必須是連續打包的、按照定義的UTF-8編碼字元串
UNSUBSCRIBE報文的有效載荷必須至少包含一個消息過濾器。沒有有效載荷的UNSUBSCRIBE報文是違反協議的。
響應:
UNSUBSCRIBE報文提供的主題過濾器(無論是否包含通配符)必須與服務端持有的這個客 戶端的當前主題過濾器集合逐個字元比較。如果有任何過濾器完全匹配,那麼它(服務端)自己的訂閱將被刪除,否則不會有進一步的處理。
如果服務端刪除了一個訂閱:
——它必須停止分發任何新消息給這個客戶端 []。
——它必須完成分發任何已經開始往客戶端發送的QoS 1和QoS 2的消息 []。
——它可以繼續發送任何現存的准備分發給客戶端的緩存消息。
服務端必須發送UNSUBACK報文響應客戶端的UNSUBSCRIBE請求。UNSUBACK報文必須包含和UNSUBSCRIBE報文相同的報文標識符 。即使沒有刪除任何主題訂閱,服務端也必須發送一個UNSUBACK響應 。
如果服務端收到包含多個主題過濾器的UNSUBSCRIBE報文,它必須如同收到了一系列的多個UNSUBSCRIBE報文一樣處理那個報文,除了將它們的響應合並到一個單獨的UNSUBACK報文外。
服務端發送UNSUBACK報文給客戶端用於確認收到UNSUBSCRIBE報文。
UNSUBACK報文沒有有效載荷。
客戶端發送PINGREQ報文給服務端的。用於:
1. 在沒有任何其它控制報文從客戶端發給服務的時,告知服務端客戶端還活著。
2. 請求服務端發送 響應確認它還活著。
3. 使用網路以確認網路連接沒有斷開。
保持連接(Keep Alive)處理中用到這個報文。
——PINGREQ報文沒有可變報頭。
——PINGREQ報文沒有有效載荷。
響應:
服務端必須發送 PINGRESP報文響應客戶端的PINGREQ報文。
服務端發送PINGRESP報文響應客戶端的PINGREQ報文。表示服務端還活著。
保持連接(Keep Alive)處理中用到這個報文。
PINGRESP報文沒有可變報頭。
PINGRESP報文沒有有效載荷。
DISCONNECT報文是客戶端發給服務端的最後一個控制報文。表示客戶端正常斷開連接。
DISCONNECT報文沒有可變報頭。
DISCONNECT報文沒有有效載荷。
響應:
客戶端發送DISCONNECT報文之後:
—— 必須關閉網路連接 [MQTT-3.14.4-1]。
——不能通過那個網路連接再發送任何控制報文。
服務端在收到DISCONNECT報文時:
——必須丟棄任何與當前連接關聯的未發布的遺囑消息。
——應該關閉網路連接,如果客戶端 還沒有這么做。
為了提供服務質量保證,客戶端和服務端有必要存儲會話狀態。在整個會話期間,客戶端和服務端都必須存儲會話狀態 。會話必須至少持續和它的活躍網路連接同樣長的時間。服務端的保留消息不是會話狀態的組成部分。服務端應該保留那種消息直到客戶端刪除它。
MQTT協議要求基礎傳輸層能夠提供有序的、可靠的、雙向傳輸(從客戶端到服務端 和從服務端到客戶端)的位元組流。
無連接的網路傳輸協議如UDP是不支持的,因為他們可能會丟失數據包或對數據包重排序。
MQTT按照這里定義的服務質量 (QoS) 等級分發應用消息。分發協議是對稱的,在下面的描述中,客戶端和服務端既可以是發送者也可以是接收者。分發協議關注的是從單個發送者到單個接收者的應用消息。服務端分發應用消息給多個客戶端時,每個客戶端獨立處理。分發給客戶端的出站應用消息和入站應用消息的QoS等級可能是不同的。
qos0:最多分發一次
qos1:至少分發一次
qos2:僅分發一次
客戶端設置清理會話(CleanSession)標志為0重連時,客戶端和服務端必須使用原始的報文標識符重發任何未確認的PUBLISH報文(如果QoS>0)和PUBREL報文 [MQTT-4.4.0-1]。這是唯一要求客戶端或服務端重發消息的情況。
服務端接管入站應用消息的所有權時,它必須將消息添加到訂閱匹配的客戶端的會話狀態。正常情況下,客戶端收到發送給它的訂閱的消息。客戶端也可能收到不是與它的訂閱精確匹配的消息。如果服務端自動給客戶端分配了一個訂閱,可能發生這種情況。正在處理UBSUBSCRIBE請求時也可能收到消息。客戶端必須按照可用的服務質量(QoS)規則確認它收到的任何PUBLISH報文,不管它選擇是否處理報文包含的應用消息 。
實現本章定義的協議流程時,客戶端必須遵循下列規則:
重發任何之前的PUBLISH報文時,必須按原始PUBLISH報文的發送順序重發(適用於QoS 1和QoS 2消息)[MQTT-4.6.0-1]。
——必須按照對應的PUBLISH報文的順序發送PUBACK報文(QoS 1消息)。
——必須按照對應的PUBLISH報文的順序發送PUBREC報文(QoS 2消息。
——必須按照對應的PUBREC報文的順序發送PUBREL報文(QoS 2消息)。
服務端必須默認認為每個主題都是有序的。它可以提供一個管理功能或其它機制,以允許將一個或多個主題當作是無序的 。
服務端處理發送給有序主題的消息時,必須按照上面的規則將消息分發給每個訂閱者。此外,它必須按照從客戶端收到的順序發送PUBLISH報文給消費者(對相同的主題和QoS)。
斜杠(『/』 U+002F)用於分割主題的每個層級,為主題名提供一個分層結構.
數字標志(『#』 U+0023)是用於匹配主題中任意層級的通配符。
加號 (『+』 U+002B) 是只能用於單個主題層級匹配的通配符。
服務端不能將 $ 字元開頭的主題名匹配通配符 (#或+) 開頭的主題過濾器.
$SYS/ 被廣泛用作包含伺服器特定信息或控制介面的主題的前綴。
應用不能使用 $ 字元開頭的主題。
訂閱 「#」 的客戶端不會收到任何發布到以 「$」 開頭主題的消息。
訂閱 「+/monitor/Clients」 的客戶端不會收到任何發布到 「$SYS/monitor/Clients」 的消息。
訂閱 「$SYS/#」 的客戶端會收到發布到以 「$SYS/」 開頭主題的消息。
訂閱 「$SYS/monitor/+」 的客戶端會收到發布到 「$SYS/monitor/Clients」 主題的消息。
如果客戶端想同時接受以 「$SYS/」 開頭主題的消息和不以 $ 開頭主題的消息,它需要同
時訂閱 「#」 和 「「$SYS/#」。
除非另有說明,如果服務端或客戶端遇到了協議違規的行為,它必須關閉傳輸這個協議違規控制報文的網路連接.
MQTT方案通常部署在不安全的通信環境中。在這種情況下,協議實現通常需要提供這些機制:
——用戶和設備身份認證
——服務端資源訪問授權
——MQTT控制報文和內嵌應用數據的完整性校驗
——MQTT控制報文和內嵌應用數據的隱私控制
作為傳輸層協議,MQTT僅關注消息傳輸,提供合適的安全功能是實現者的責任。使用TLS[RFC5246] 是比較普遍的選擇。
廣泛採用高級加密標准 [AES] 數據加密標准 [DES] 。
推薦使用為受限的低端設備特別優化過的輕量級加密國際標准 ISO 29192 [ISO29192] 。
如果MQTT在WebSocket [RFC6455] 連接上傳輸,必須滿足下面的條件:
——MQTT控制報文必須使用WebSocket二進制數據幀發送。如果收到任何其它類型的數據幀,接收者必須關閉網路連接 。
——單個WebSocket數據幀可以包含多個或者部分MQTT報文。接收者不能假設MQTT控制報文按WebSocket幀邊界對齊 。
——客戶端必須將字元串 mqtt 包含在它提供的WebSocket子協議列表裡 。
——服務端選擇和返回的WebSocket子協議名必須是 。
——用於連接客戶端和伺服器的WebSocket URI對MQTT協議沒有任何影響。
MQTT規范定義了MQTT客戶端實現和MQTT服務端實現的一致性要求
MQTT實現可以同時是MQTT客戶端和MQTT服務端。接受入站連接和建立到其它服務端的出站連接的服務端必須同時符合MQTT客戶端和MQTT服務端的要求 。
為了與任何其它的一致性實現交互操作,一致性實現不能要求使用在本規范之外定義的任何擴展 。
附錄:
控制報文類型
byte1:標志位flags:
I. Mqtt介紹一
MQTT ( 消息隊列遙測傳輸 )是ISO 標准(ISO/IEC PRF 20922)下基於發布/訂閱範式的消息協議。它工作在 TCP/IP協議族 上,是為硬體性能低下的遠程設備以及網路狀況糟糕的情況下而設計的發布/訂閱型消息協議,為此,它需要一個 消息中間件 。
MQTT是一個基於客戶端-伺服器的消息發布/訂閱傳輸協議。MQTT協議是輕量、簡單、開放和易於實現的,這些特點使它適用范圍非常廣泛。在很多情況下,包括受限的環境中,如:機器與機器(M2M)通信和物聯網(IoT)。其在,通過衛星鏈路通信感測器、偶爾撥號的醫療設備、智能家居、及一些小型化設備中已廣泛使用。
MQTT協議是為大量計算能力有限,且工作在低帶寬、不可靠的網路的遠程感測器和控制設備通訊而設計的協議,它具有以下主要的幾項特性:
實現MQTT協議需要客戶端和伺服器端通訊完成,在通訊過程中,MQTT協議中有三種身份: 發布者(Publish) 、 代理(Broker) (伺服器) 、訂閱者(Subscribe) 。其中,消息的發布者和訂閱者都是客戶端,消息代理是伺服器,消息發布者可以同時是訂閱者。
MQTT傳輸的消息分為: 主題(Topic)和負載(payload) 兩部分:
MQTT伺服器以稱為"消息代理"(Broker),可以是一個應用程序或一台設備。它是位於消息發布者和訂閱者之間,它可以:
在MQTT協議中,一個MQTT數據包由: 固定頭(Fixed header)、可變頭(Variable header)、消息體(payload) 三部分構成。MQTT數據包結構如下:
(1) 固定頭(Fixed header) 。存在於所有MQTT數據包中,表示數據包類型及數據包的分組類標識。
(2) 可變頭(Variable header) 。存在於部分MQTT數據包中,數據包類型決定了可變頭是否存在及其具體內容。
(3) 消息體(Payload) 。存在於部分MQTT數據包中,表示客戶端收到的具體內容。
固定頭存在於所有MQTT數據包中,其結構如下:
相於一個4位的無符號值,類型、取值及描述如下
在不使用標識位的消息類型中,標識位被作為保留位。如果收到無效的標志時,接收端必須關閉網路連接:
Payload消息體位MQTT數據包的第三部分,包含CONNECT、SUBSCRIBE、SUBACK、UNSUBSCRIBE四種類型的消息: