MQTT協議
MQTT是輕量級基於代理的發佈/訂閱的消息傳輸協議,它可以通過很少的代碼和帶寬和遠程設備連接。例如通過衛星和代理連接,通過撥號和醫療保健提供者連接,以及在一些自動化或小型設備上,而且由於小巧,省電,協議開銷小和能高效的向一和多個接收者傳遞信息,故同樣適用於稱動應用設備上。
Mqtt協議特點:
1.使用發佈/訂閱消息模式,提供一對多的消息發佈,解除應用程序耦合。
2.協議簡單,最小的頭部只需2個字節,特別適合於嵌入式中。
3.對負載內容屏蔽的消息傳輸。
4.使用 TCP/IP 提供網絡連接。
5.有三種消息發佈服務質量:
(1) “至多一次”,消息發佈完全依賴底層 TCP/IP 網絡。會發生消息丟失或重複。這一級別可用於如下情況,環境傳感器數據,丟失一次讀記錄無所謂,因爲不久後還會有第二次發送。
(2) “至少一次”,確保消息到達,但消息重複可能會發生。
(3) “只有一次”,確保消息到達一次。這一級別可用於如下情況,在計費系統中,消息重複或丟失會導致不正確的結果。
6. 使用 Last Will 和 Testament 特性通知有關各方客戶端異常中斷的機制。
PUB/SUB 模型
Mqtt結構
固定頭部,使用兩個字節,共16位:
bit |
7 |
6 |
5 |
4 |
3 |
2 |
1 |
0 |
byte 1 |
Message Type |
DUP flag |
QoS level |
RETAIN |
||||
byte 2 |
Remaining Length |
消息類型
使用4位二進制表示,可代表16種消息類型
Mnemonic |
Enumeration |
Description |
Reserved |
0 |
Reserved----保留待用 |
CONNECT |
1 |
Client request to connect to Server----客戶端連接請求 |
CONNACK |
2 |
Connect Acknowledgment----連接反饋 |
PUBLISH |
3 |
Publish message-----發佈消息 |
PUBACK |
4 |
Publish Acknowledgment-----發佈反饋 |
PUBREC |
5 |
Publish Received (assured delivery part 1)----發佈消息被接收 |
PUBREL |
6 |
Publish Release (assured delivery part 2) |
PUBCOMP |
7 |
Publish Complete (assured delivery part 3)----發佈消息完成 |
SUBSCRIBE |
8 |
Client Subscribe request----客戶端訂閱 |
SUBACK |
9 |
Subscribe Acknowledgment----訂閱反饋 |
UNSUBSCRIBE |
10 |
Client Unsubscribe request----客戶端解除訂閱 |
UNSUBACK |
11 |
Unsubscribe Acknowledgment----接觸訂閱反饋 |
PINGREQ |
12 |
PING Request-------心跳檢測 |
PINGRESP |
13 |
PING Response----心跳反饋 |
DISCONNECT |
14 |
Client is Disconnecting----客戶端斷開連接 |
Reserved |
15 |
Reserved----保留待用 |
消息類型使用的場景這裏我使用的是wireshark 抓包工具
首先我先來測試demo1也就是消息類型QOS
代碼中我沒有設置QOS類型所以他會默認選0;
(至多發送一次,發送即丟棄。沒有確認消息,也不知道對方是否收到)
我本機ip是192.168.101.248 向192.168.56.3服務器發送訂閱請求如上圖mqtt使用的是TCP/IP網絡傳輸所以也要經過三次握手原則。在客戶機和服務器之間建立正常的TCP網絡連接時,客戶機首先發出一個SYN消息,服務器使用SYN+ACK應答表示接收到了這個消息,最後客戶機再以ACK消息響應。這樣在客戶機和服務器之間才能建立起可靠的TCP連接,數據纔可以在客戶機和服務器之間傳遞,也就是上面mqtt的特點:使用 TCP/IP 提供網絡連接。
名詞解釋:SYN表示建立連接, FIN表示關閉連接, ACK表示響應, PSH表示有DATA數據傳輸, RST表示連接重置。
在Connect 中可以看到我的發佈者ID爲 demo也就是我代碼中所寫的:
第二個Connect Ack 內容
可以看到是0;因爲我沒有設置QOS消息方法所以默認是0;0代表最多1次,發完就丟棄,不保證消息是否收到。下面可以參考QOS服務質量資料。
Publish message 也就是我們發送的消息了
因爲這裏漢字是採用8進製表示的,如果轉碼需要經過轉爲16進制每兩位代表一個漢字共四位,從中間的topic就可以看出那個就是我們發送的消息了。
PUBACK
服務器向我們返回的信息也就是這個信息的ID號了。
DISCONNECT
-客戶端斷開連接,因爲我在代碼中最後執行了斷開連接的方法所以會通知服務器。代碼如圖:
QOS 1
(所有QoS level 1都要在可變頭部中附加一個16位的消息ID提供SUBSCRIBE(訂閱)和UNSUBSCRIBE(退訂)消息使用。針對消息的發佈,Qos level 1,意味着消息至少被傳輸一次。發送者若在一段時間內接收不到PUBACK消息,發送者需要打開DUB標記爲1,然後重新發送PUBLISH消息。因此會導致接收方可能會收到兩次PUBLISH消息)
因爲連接的是時候可以接受到mqtt服務器的回覆,在送出去消息的時候要讓服務器回執發送不到客戶端,這個測試環境不好測試所以就沒例子。
QOS 2
從上圖可以看出首先發佈者連接mqtt服務器,然後得到服務器的回覆,發佈者就開始發送消息,服務器這邊回覆 publish received也就是告訴發佈者你的消息我收到了,接着發佈告訴服務器,我收到你的回覆了,那條消息你可以刪除了,然後服務器又回覆發佈者我做完了,最後發佈者斷開連接。
QOS 3 (mqtt 預留的信息類型待用。這裏就沒測試的必要了)
DUP flag(打開標誌)
保證消息可靠傳輸,默認爲0,只佔用一個字節,表示第一次發送。不能用於檢測消息重複發送等。只適用於客戶端或服務器端嘗試重發PUBLISH, PUBREL, SUBSCRIBE 或 UNSUBSCRIBE消息,注意需要滿足以下條件:當QoS > 0 消息需要回復確認此時,在可變頭部需要包含消息ID。當值爲1時,表示當前消息先前已經被傳送過。
QoS(Quality of Service,服務質量)
使用兩個二進制表示PUBLISH類型消息:
QoS value |
bit 2 |
bit 1 |
Description |
||
0 |
0 |
0 |
至多一次 |
發完即丟棄 |
<=1 |
1 |
0 |
1 |
至少一次 |
需要確認回覆 |
>=1 |
2 |
1 |
0 |
只有一次 |
需要確認回覆 |
=1 |
3 |
1 |
1 |
待用,保留位置 |
RETAIN(保持)
僅針對PUBLISH消息。不同值,不同含義:
1:表示發送的消息需要一直持久保存(不受服務器重啓影響),不但要發送給當前的訂閱者,並且以後新來的訂閱了此Topic name的訂閱者會馬上得到推送。
這裏我設置爲true 也就是1,測試的topic爲testRetain
這裏我先讓一個訂閱者jieshou訂閱一個testRetain,現在來測試下發布者發佈一條消息的結果。這裏發佈者已經發布了一條消息
已經訂閱的訂閱者已經接受到了消息
現在再來啓動第二個訂閱者訂閱者叫jieshou2
新來的訂閱者也接受到了該信息。備註:新來乍到的訂閱者,只會取出最新的一個RETAIN flag = 1的消息推送。
0:僅僅爲當前訂閱者推送此消息。
假如服務器收到一個空消息體(zero-length payload)、RETAIN = 1、已存在Topic name的PUBLISH消息,服務器可以刪除掉對應的已被持久化的PUBLISH消息。
發送者發送一條消息給訂閱方。
開啓第二個新訂閱者
新的訂閱者沒有接收到消息
Remaining Length(剩餘長度)
在當前消息中剩餘的byte(字節)數,包含可變頭部和負荷(稱之爲內容/body,更爲合適)。單個字節最大值:01111111,16進制:0x7F,10進製爲127。單個字節爲什麼不能是11111111(0xFF)呢?因爲MQTT協議規定,第八位(最高位)若爲1,則表示還有後續字節存在。同時MQTT協議最多允許4個字節表示剩餘長度。那麼最大長度爲:0xFF,0xFF,0xFF,0x7F,二進制表示爲:11111111,11111111,11111111,01111111,十進制:268435455 byte=261120KB=256MB=0.25GB 四個字節之間值的範圍:
Digits |
From |
To |
1 |
0 (0x00) |
127 (0x7F) |
2 |
128 (0x80, 0x01) |
16 383 (0xFF, 0x7F) |
3 |
16 384 (0x80, 0x80, 0x01) |
2 097 151 (0xFF, 0xFF, 0x7F) |
4 |
2 097 152 (0x80, 0x80, 0x80, 0x01) |
268 435 455 (0xFF, 0xFF, 0xFF, 0x7F) |
固定頭部僅定義了消息類型和一些標誌位,一些消息的元數據,需要放入可變頭部中。可變頭部內容字節長度 + Playload/負荷字節長度 = 剩餘長度,這個是需要牢記的。可變頭部,包含了協議名稱,版本號,連接標誌,用戶授權,心跳時間等內容。
消息體主要是爲配合固定/可變頭部命令(比如CONNECT可變頭部User name標記若爲1則需要在消息體中附加用戶名稱字符串)而存在。
CONNECT/SUBSCRIBE/SUBACK/PUBLISH等消息有消息體。PUBLISH的消息體以二進制形式對待。
我這裏使用的是paho mqtt框架所以消息體中二進制轉義等一系列功夫就不需要關心太多,只需要記住mqtt協議消息體最大容量和消息的表現形式就可以了。具體轉義實現可以參考:http://www.blogjava.net/yongboy/archive/2014/02/07/409587.html
當我強制關閉客戶端程序的時候
也就是mqtt協議特點 Last Will 和 Testament 特性通知有關各方客戶端異常中斷的機制。 ·
其他問題參考文獻:http://www.blogjava.net/yongboy/archive/2014/02/07/409587.html,
http://blog.csdn.net/u013944791/article/details/44218471