前言
MQTT(Message Queue Telemetry Transport),遙測傳輸協(xié)議,提供訂閱/發(fā)布模式,更為簡約、輕量,易于使用,針對受限環(huán)境(帶寬低、網(wǎng)絡(luò)延遲高、網(wǎng)絡(luò)通信不穩(wěn)定),可以簡單概括為物聯(lián)網(wǎng)打造,官方總結(jié)特點(diǎn)如下:
1.使用發(fā)布/訂閱消息模式,提供一對多的消息發(fā)布,解除應(yīng)用程序耦合。
2. 對負(fù)載內(nèi)容屏蔽的消息傳輸。
3. 使用 TCP/IP 提供網(wǎng)絡(luò)連接。
4. 有三種消息發(fā)布服務(wù)質(zhì)量:
“至多一次”,消息發(fā)布完全依賴底層 TCP/IP 網(wǎng)絡(luò)。會發(fā)生消息丟失或重復(fù)。這一級別可用于如下情況,環(huán)境傳感器數(shù)據(jù),丟失一次讀記錄無所謂,因?yàn)椴痪煤筮€會有第二次發(fā)送。
“至少一次”,確保消息到達(dá),但消息重復(fù)可能會發(fā)生。
“只有一次”,確保消息到達(dá)一次。這一級別可用于如下情況,在計費(fèi)系統(tǒng)中,消息重復(fù)或丟失會導(dǎo)致不正確的結(jié)果。
5. 小型傳輸,開銷很小(固定長度的頭部是 2 字節(jié)),協(xié)議交換最小化,以降低網(wǎng)絡(luò)流量。
6. 使用 Last Will 和 Testament 特性通知有關(guān)各方客戶端異常中斷的機(jī)制。
在沉寂了四年之后, MQTT 3.1.1規(guī)范 于2014年10月30號正式發(fā)布,與此同時MQTT 3.1.1已成為OASIS(結(jié)構(gòu)化信息標(biāo)準(zhǔn)促進(jìn)組織)開放物聯(lián)網(wǎng)消息傳遞協(xié)議標(biāo)準(zhǔn)( 連接1 連接2 ),換種說法就是MQTT 3.1.1已升級為國際物聯(lián)網(wǎng)標(biāo)準(zhǔn)。
細(xì)心的朋友一定會發(fā)現(xiàn),原來MQTT 3.1規(guī)范是IBM的協(xié)議,到了MQTT 3.1.1規(guī)范 后就變成了OASIS的標(biāo)準(zhǔn)。
中文版的文檔查看以下的鏈接:
MQTT協(xié)議v3.1中文版
MQTT協(xié)議v3.1.1中文版
協(xié)議介紹
固定頭部
固定頭部,使用兩個字節(jié),共16位:
Byte 1 消息類型和標(biāo)志字段,消息類型(4-7),使用4位二進(jìn)制表示,可代表16種消息類型。
Byte 2 剩余長度字段(至少1個字節(jié),最多4個字節(jié)),采用big-endian模式存儲。
消息類型
除去0和15位置屬于保留待用,共14種消息事件類型。
DUP flag(打開標(biāo)志)
保證消息可靠傳輸,默認(rèn)為0,只占用一個字節(jié),表示第一次發(fā)送。不能用于檢測消息重復(fù)發(fā)送等。只適用于客戶端或服務(wù)器端嘗試重發(fā)PUBLISH, PUBREL, SUBSCRIBE 或 UNSUBSCRIBE消息,注意需要滿足以下條件:
當(dāng) QoS > 0
消息需要回復(fù)確認(rèn)
此時,在可變頭部需要包含消息ID。當(dāng)值為1時,表示當(dāng)前消息先前已經(jīng)被傳送過。
QoS(Quality of Service,服務(wù)質(zhì)量)
使用兩個二進(jìn)制表示PUBLISH類型消息:
RETAIN(保持)
僅針對PUBLISH消息。不同值,不同含義:
1:表示發(fā)送的消息需要一直持久保存(不受服務(wù)器重啟影響),不但要發(fā)送給當(dāng)前的訂閱者,并且以后新來的訂閱了此Topic name的訂閱者會馬上得到推送。
備注:新來乍到的訂閱者,只會取出最新的一個RETAIN flag = 1的消息推送。
0:僅僅為當(dāng)前訂閱者推送此消息。
假如服務(wù)器收到一個空消息體(zero-length payload)、RETAIN = 1、已存在Topic name的PUBLISH消息,服務(wù)器可以刪除掉對應(yīng)的已被持久化的PUBLISH消息。
Remaining Length(剩余長度)
在當(dāng)前消息中剩余的byte(字節(jié))數(shù),包含可變頭部和負(fù)荷(稱之為內(nèi)容/body,更為合適)。單個字節(jié)最大值:01111111,16進(jìn)制:0x7F,10進(jìn)制為127。單個字節(jié)為什么不能是11111111(0xFF)呢?因?yàn)镸QTT協(xié)議規(guī)定,第八位(最高位)若為1,則表示還有后續(xù)字節(jié)存在。
同時MQTT協(xié)議最多允許4個字節(jié)表示剩余長度。那么最大長度為:0xFF,0xFF,0xFF,0x7F。
二進(jìn)制表示為:11111111,11111111,11111111,01111111
十進(jìn)制:268435455 byte=261120KB=256MB=0.25GB
四個字節(jié)之間值的范圍:
可變頭部
固定頭部僅定義了消息類型和一些標(biāo)志位,一些消息的元數(shù)據(jù),需要放入可變頭部中。可變頭部內(nèi)容字節(jié)長度 + Payload/負(fù)荷字節(jié)長度 = 剩余長度,這個是需要牢記的。可變頭部,包含了協(xié)議名稱,版本號,連接標(biāo)志,用戶授權(quán),心跳時間等內(nèi)容,這部分和后面要講到的CONNECT消息類型,有重復(fù),暫時略過。
Payload/消息體/負(fù)荷
消息體主要是為配合固定/可變頭部命令(比如CONNECT可變頭部User name標(biāo)記若為1則需要在消息體中附加用戶名稱字符串)而存在。
CONNECT/SUBSCRIBE/SUBACK/PUBLISH等消息有消息體。PUBLISH的消息體以二進(jìn)制形式對待。
請記住MQTT協(xié)議只允許在PUBLISH類型消息體中使用自定義特性,在固定/可變頭部想加入自定義私有特性,就免了吧。這也是為了協(xié)議免于流于形式,變得很分裂也為了兼顧現(xiàn)有客戶端等。比如支持壓縮等,那就可以在Payload中定義數(shù)據(jù)支持,在應(yīng)用中進(jìn)行讀取處理。這部分會在后面的文章中詳細(xì)論述。
消息標(biāo)識符/消息ID
固定頭中的QoS level標(biāo)志值為1或2時才會在:PUBLISH,PUBACK,PUBREC,PUBREL,PUBCOMP,SUBSCRIBE,SUBACK,UNSUBSCRIBE,UNSUBACK等消息的可變頭中出現(xiàn)。
一個16位無符號位的short類型值(值不能為 0,0做保留作為無效的消息ID),僅僅要求在一個特定方向(服務(wù)器發(fā)往客戶端為一個方向,客戶端發(fā)送到服務(wù)器端為另一個方向)的通信消息中必須唯一。比如客戶端發(fā)往服務(wù)器,有可能存在服務(wù)器發(fā)往客戶端會同時存在重復(fù),但不礙事。
可變頭部中,需要兩個字節(jié)的順序是MSB(Most Significant Bit) LSB(Last/Least Significant Bit),翻譯成中文就是,最高有效位,最低有效位。最高有效位在最低有效位左邊/上面,表示這是一個大端字節(jié)/網(wǎng)絡(luò)字節(jié)序,符合人的閱讀習(xí)慣,高位在最左邊。
小結(jié)
掌握固定頭部的QoS level、RETAIN標(biāo)記、可變頭部的Connect flags作用和意義,對總體理解MQTT作用很大。下面列舉了一些常用的操作:
CONNECT
TCP連接建立完畢后,Client向Server發(fā)出一個Request。
如果一段時間內(nèi)接收不到Server的Response,則關(guān)閉socket,重新建立一個session連接。
如果一個ClientID已經(jīng)與服務(wù)器連接,則持有同樣ClientID的舊有連接必須由服務(wù)器關(guān)閉后,新建立才能建立。
CONNACK
Server發(fā)出Response響應(yīng)。
0x00 Connection Accepted
0x01 Connection Refused: unacceptable protocol version
0x02 Connection Refused: identifier rejected
0x03 Connection Refused: server unavailable
0x04 Connection Refused: bad user name or password
0x05 Connection Refused: not authorized
PUBLISH 發(fā)布消息
Client/Servier均可以進(jìn)行PUBLISH。
publish message 應(yīng)該包含一個TopicName(Subject/Channel),即訂閱關(guān)鍵詞。
關(guān)于Topic通配符
- /:用來表示層次,比如a/b,a/b/c。
- #:表示匹配>=0個層次,比如a/#就匹配a/,a/b,a/b/c。
單獨(dú)的一個#表示匹配所有。不允許 a#和a/#/c。 - +:表示匹配一個層次,例如a/+匹配a/b,a/c,不匹配a/b/c。
單獨(dú)的一個+是允許的,a+不允許,a/+/b不允許
PUBACK 發(fā)布消息后的確認(rèn)
QoS=1時,Server向Client發(fā)布該確認(rèn)(Client收到確認(rèn)后刪除),訂閱者向Server發(fā)布確認(rèn)。
PUBREC / PUBREL / PUBCOMP
QoS=2時
1. Server->Client發(fā)布PUBREC(已收到);
2. Client->Server發(fā)布PUBREL(已釋放);
3. Server->Client發(fā)布PUBCOMP(已完成),Client刪除msg;訂閱者也會向Server發(fā)布類似過程確認(rèn)。
PINGREQ / PINGRES 心跳
Client有責(zé)任發(fā)送KeepAliveTime時長告訴給Server。在一個時長內(nèi),發(fā)送PINGREQ,Server發(fā)送PINGRES確認(rèn)。
Server在1.5個時長內(nèi)未收到PINGREQ,就斷開連接。
Client在1個時長內(nèi)未收到PINGRES,斷開連接。
一般來說,時長設(shè)置為幾個分鐘。最大18小時,0表示一直未斷開。
Clean Session
如果為false(flag=0),Client斷開連接后,Server應(yīng)該保存Client的訂閱信息。
如果為true(flag=1),表示Server應(yīng)該立刻丟棄任何會話狀態(tài)信息。