文章目錄
- 前言
- 簡述
- MQTT綜述
- 相關術語
- 網絡連接(Network Connection)
- 應用消息(Application Message)
- 客戶端(Client)
- 服務端(Server)
- 訂閱(Subscription)
- 主題過濾器(Topic Filter)
- 會話(Session)
- MQTT控制報文(MQTT Control Packet)
- 共享訂閱(Shared Subscription,MQTT 5)
- 通配符訂閱(Wildcard Subscription)
- 主題名(Topic Name)
- 無效報文(Malformed Packet)
- 協議錯誤(Protocol Error)
- 遺囑消息(Will Message)
- 不允許的Unicode代碼點(Disallowed Unicode code point)
- 數據表示
- 報文結構
- 總結
前言
本次博文即後續一系列博文,將就MQTT協議3.1.1和MQTT協議5.0進行介紹和描述,主要是依據官方文檔進行翻譯和再次編輯,將同步區別部分進行細化。另外還會推薦一些mqtt的實現,由於mqtt 5今年新出,mqtt 3版本的broker已經有許多優秀的開源項目,mqtt5完全實現的broker較少。
MQTT是物聯網的協議,英文全稱爲,Message Queuing Telemetry Transport,被翻譯爲,消息隊列遙測傳輸。目前爲止,如今最常用的是MQTT3.1.1協議,因爲是在2014年10月29日發佈了最終稿,MQTT5.0的草案從2017年至今年上半年3月7號才最終確定,只是5.0的第一個版本。MQTT的初始第一版由IBM在1999年發佈。
簡述
MQTT是一個客戶端服務端架構的發佈/訂閱模式的消息傳輸協議,構建於TCP/IP或者其他有序、可靠、雙向連接的網絡連接上。
它的特點有:
-
使用發佈/訂閱消息模式,提供了一對多的消息分發和應用之間的解耦。
-
消息傳輸不需要知道負載內容
-
提供三種等級的服務質量(QoS)
- QoS0: “最多一次(At most once)”,盡操作環境所能提供的最大努力分發消息。消息可能會丟失。例如,這個等級可用於環境傳感器數據,單次的數據丟失沒關係,因爲不久之後會再次發送。
- QoS1: “至少一次”,保證消息可以到達,但是可能會重複。
- QoS2: “僅一次”,保證消息只到達一次。例如,這個等級可用在一個計費系統中,這裏如果消息重複或丟失會導致不正確的收費。
-
很小的傳輸消耗和協議數據交換,最大限度減少網絡流量
-
異常連接斷開發生時,能通知到相關各方
MQTT綜述
相關術語
網絡連接(Network Connection)
MQTT使用的底層傳輸協議基礎設施。
- 客戶端使用它連接服務端
- 提供有序的、可靠的、雙向字節流傳輸
應用消息(Application Message)
MQTT協議通過網絡傳輸應用數據。
MTQQ3.1.1
MQTT傳輸傳輸的應用消息通過,包含關聯的服務質量(QoS)和主題(Topic)名。
MQTT5.0
當MQTT傳輸應用程序消息,包含有效負載數據(payload data),服務質量(QoS),屬性的集合(Properties)和主題名(Topic)。
客戶端(Client)
使用MQTT的程序或設備。客戶端總是通過網絡連接到服務端。它可以
- 發佈應用消息給其它相關的客戶端
- 訂閱以請求接受相關的應用消息
- 取消訂閱以移除接受應用消息的請求
- 與服務端斷開連接
服務端(Server)
一個程序或設備,作爲發送應用消息的客戶端和請求訂閱的客戶端之間的中介。服務端
- 接受來自客戶端的網絡連接
- 接受客戶端發佈的應用消息
- 處理客戶端的訂閱和取消訂閱請求
- 轉發應用消息給符合條件的客戶端訂閱
訂閱(Subscription)
訂閱包含一個主題過濾器(Topic Filter)和一個最大的服務質量(QoS)等級。訂閱與單個會話(Session)關聯。會話可以包含多於一個的訂閱。會話的每個訂閱都有一個不同的主題過濾器。
主題過濾器(Topic Filter)
訂閱中包含的一個表達式,用於表示相關的一個或多個主題。主題過濾器可以包含通配符。
會話(Session)
客戶端和服務端之間的狀態交互。一些會話持續時長與網絡連接一樣,另一些可以在客戶端和服務端的多個連續網絡連接間擴展。
MQTT控制報文(MQTT Control Packet)
通過網絡連接發送的信息數據包。MQTT規範定義了十四種不同類型的控制報文,其中一個(PUBLISH報文)用於傳輸應用消息。
MQTT5.0增加至15種,其中增加了AUTH。即Authentication exchange
共享訂閱(Shared Subscription,MQTT 5)
一個共享訂閱包含一個主題過濾器(Topic Filter)和一個最大的服務質量(QoS)等級。一個共享訂閱可以與多個訂閱會話相關聯,便於支持大範圍消息交換模式。與共享訂閱匹配的應用消息僅發送到與這些會話之一相關聯的客戶端。一個會話可以包括多個共享訂閱,可以同時包含共享訂閱與非共享訂閱。
針對共享訂閱的個人理解(qiwei.tan)
由於是個人理解,所以有誤部分,歡迎指出
共享訂閱針對場景應是數據的生產者遠超出數據消費者數量,而且同一條數據(消息)只需要被任意其中一個消費者處理一次。主要是實現消費者數量處理消息的均衡負載。
比如有一個發佈者發佈四條消息,只需要處理一次,此時有四個訂閱者共享這四條消息,實現MQTT的broker端只需要把消息分別發送到四個訂閱者便可以實現消息的處理。
通配符訂閱(Wildcard Subscription)
通配符訂閱是具有包含一個或多個通配符的主題過濾器的訂閱。 這允許訂閱匹配多個主題名稱。
主題名(Topic Name)
附加到應用程序消息的標籤,該標籤與服務器已知的訂閱相匹配。
無效報文(Malformed Packet)
根據規範不能被正確解析的控制報文。
協議錯誤(Protocol Error)
解析數據包後發現錯誤,發現該錯誤包含協議不允許的數據或與客戶端或服務器的狀態不一致的數據。
遺囑消息(Will Message)
在網絡連接正常關閉的情況下,服務器在網絡連接關閉後發佈的應用程序消息。
不允許的Unicode代碼點(Disallowed Unicode code point)
不應包含在UTF-8編碼字符串中的Unicode控制代碼和Unicode非字符集。
代碼點(code point)是指與一個編碼表中的某個字符對應的代碼值。
數據表示
單字節數據
字節中的位從0到7。第7位是最高有效位,第0位是最低有效位。
雙字節整數
雙字節整數數值是使用大端序(big-endian,高位字節在低位字節前面)的16位無符號整數。這意味着一個16位的字在網絡上表示爲最高有效位(MSB),後面跟着最低有效位(LSB)。
當物理單位長度大於1個字節時,需要區分字節順序。關於大小端序,可以點擊查看這篇博客。
四字節整數(MQTT5.0新增)
四字節整數數據值是按大端序(big-endian,高位字節在低位字節前面)的32位無符號整數:高位字節先於連續的低位字節。 這意味着一個32位字在網絡上顯示爲最高有效字節(MSB),然後是下一個最高有效字節(MSB),然後是下一個最高有效字節(MSB),然後是最低有效字節(LSB)。
UTF-8編碼字符串
後續描述的MQTT控制報文中的文本字段編碼爲 UTF-8 格式的字符串。UTF-8 是Unicode字符的有效編碼,優化ASCII字符的編碼以支持基於文本的通信。
除非另有說明,否則所有UTF-8編碼的字符串長度都可以在0到65535字節之間。
圖例 UTF-8編碼字符串的結構
==UTF-8編碼字符串中的字符數據必須是按照Unicode規範定義的有效的UTF-8格式,並在RFC3629中進行重述。特別需要指出的是,這些數據不能包含字符碼在U+D800
和U+DFFF
之間的數據。==如果服務端或客戶端收到了一個包含無效UTF-8字符的控制報文,則它是無效報文。
==UTF-8編碼的字符串不能包含空字符U+0000
。==如果客戶端或服務端收到了一個包含U+0000
的控制報文,則它是無效報文。
數據不應包含下面列出的Unicode代碼點的編碼。 如果接收者(服務器或客戶端)接收到包含其中任何一個的MQTT控制報文,則可以將其視爲無效報文。 這些是禁止的Unicode代碼點。
U+0001..U+001F
控制字符U+007F..U+009F
控制字符- Unicode規範定義的非字符代碼點(例如
U+0FFFF
)
UTF-8編碼的序列0xEF 0xBB 0xBF
始終被解釋爲U + FEFF
(零寬度非換行空白字符),無論它出現在字符串中的什麼位置,都不得被數據報文接收者跳過或剝離。
非規範的例子
例如,字符串 A?
它是拉丁文大寫字母A,後跟代碼點U + 2A6D4
(代表中日韓統一表意文字擴展B中的字符),其編碼如下:
圖例 UTF-8編碼字符串非規範示例
可變字節整數
剩餘長度字段使用一個變長字節編碼方案,對小於 128 的值它使用單字節編碼。更大的值按下面的方式處理。低 7 位有效位用於編碼數據,最高有效位用於指示是否有更多的字節。因此每個字節可以編碼 128 個數值和一個延續位(continuation bit)。剩餘長度字段最大 4 個字節,編碼值必須使用表示該值所需的最小字節數,如下表所示。
可變字節整數的大小
字節數 | 最小值 | 最大值 |
---|---|---|
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) |
編碼方案算法
用於將非負整數X
編碼爲可變字節整數編碼方案的算法如下:
do
encodedByte = X MOD 128
X = X DIV 128
// if there are more data to encode, set the top bit of this byte
// 如果還有更多數據要編碼,請設置此字節的高位
if (X > 0)
encodedByte = encodedByte OR 128
endif
'output' encodedByte
while (X > 0)
MOD是模運算,DIV是整數除法,OR是位操作或(C語言中分別是%,/,|)
解碼算法
可變字節整數類型的解碼算法如下:
multiplier = 1
value = 0
do
encodedByte = 'next byte from stream'
value += (encodedByte AND 127) * multiplier
if (multiplier > 128*128*128)
throw Error(Malformed Variable Byte Integer)
multiplier *= 128
while ((encodedByte AND 128) != 0)
AND 是位操作與(C 語言中的&)
這個算法終止時,value 包含的就是剩餘長度的值。
二進制數據
二進制數據由一個雙字節整數指示其數據長度,因此,二進制數據的長度被限制爲0到65,535字節。
UTF-8字符串鍵值對(MQTT5.0)
UTF-8字符串對由兩個UTF-8編碼的字符串組成,用來表示名字-值對,第一個字符串表示名字,第二個字符串表示值。
所有的字符串必須遵循UTF-8字符串編碼規範,如果接受者(客戶端或者服務端)接受到一個字符串對,然而其編碼並不遵循規範,則此報文爲無效報文。
安全(MQTT5.0)
MQTT客戶端和服務器實現應提供身份驗證,授權和安全通信選項,後續會有說明。強烈建議與關鍵基礎結構,個人身份信息或其他個人或敏感信息有關的應用程序使用這些安全性功能。
報文結構
MQTT協議通過以定義的方式交換一系列MQTT控制報文來進行操作。
MQTT控制報文最多由三部分組成,始終按照以下順序顯示,如下所示。
圖例–MQTT控制報文的結構
固定報頭
每個MQTT控制包都包含一個固定報頭,如下所示。
圖例-固定報頭的格式
控制報文的類型
位置:第一個字節,二進制位 7- 4。
表示爲4位無符號值的值在下表中列出。
標誌位
固定標頭中第一個字節的剩餘4位即[3-0]位,包含特定於每種MQTT控制數據包類型的標誌,如下表所示。 如果標誌位標記爲“保留”,則保留該標誌位以備將來使用,並且必須將其設置爲列出的值。 如果收到非法的標誌,則它是無效報文。
表格-標誌位 Flag Bits
- = 控制報文的重複分發標誌
- = PUBLISH報文的服務質量等級
- = PUBLISH報文的保留標誌
剩餘長度
位置:從第2個字節開始。
剩餘長度是一個可變字節整數,表示當前控制報文中剩餘的字節數,包括可變報頭和有效載荷中的數據。 剩餘長度不包括用於編碼剩餘長度的字節數。 數據報文大小是MQTT控制數據報文中的字節總數,它等於固定報頭的長度加上剩餘長度。
非規範評註
例如,十進制數64會被編碼爲一個字節,數值是64,十六進制表示爲0x40。十進制數字321(=65+2*128)被編碼爲兩個字節,最低有效位在前。第一個字節是 65+128=193。注意最高位爲1表示後面至少還有一個字節。第二個字節是2。
非規範評註
這允許應用發送最大256MB(268,435,455)大小的控制報文。這個數值在報文中的表示是:0xFF,0xFF,0xFF,0x7F。
可變報頭
某些MQTT控制報文包含一個可變報頭部分。它在固定報頭和有效載荷之間。可變報頭的內容根據報文類型的不同而不同。可變報頭的報文標識符(Packet Identifier)字段存在於在多個類型的報文裏。
報文標識符
許多MQTT控制數據報文類型的可變報頭組件都包含一個2字節整數數據包標識符字段。 這些MQTT控制數據報文是PUBLISH(當QoS> 0),PUBACK,PUBREC,PUBREL,PUBCOMP,SUBSCRIBE,SUBACK,UNSUBSCRIBE和UNSUBACK。
圖例-報文標識符字節 Packet Identifier bytes
需要數據報文標識符的MQTT控制數據報文如下所示:
MQTT 控制報文 | 報文標識符字段 |
---|---|
CONNECT | 不需要 |
CONNACK | 不需要 |
PUBLISH | 需要(如果 QoS > 0) |
PUBACK | 需要 |
PUBREC | 需要 |
PUBREL | 需要 |
PUBCOMP | 需要 |
SUBSCRIBE | 需要 |
SUBACK | 需要 |
UNSUBSCRIBE | 需要 |
UNSUBACK | 需要 |
PINGREQ | 不需要 |
PINGRESP | 不需要 |
DISCONNECT | 不需要 |
AUTH | 不需要 |
AUTH是MQTT5.0版本的
- QoS設置爲0的PUBLISH報文不能包含報文標識符
- 客戶端每次發送一個新的SUBSCRIBE,UNSUBSCRIBE或者PUBLISH(當QoS>0時)MQTT控制報文時都必須分配一個當前未使用的非零報文標識符【16位】
- 服務端每次發送一個新的PUBLISH(當QoS>0)MQTT控制報文時都必須分配一個當前未使用的非零報文標識符
- 當客戶端處理完這個報文對應的確認後,這個報文標識符就釋放可重用。QoS 1的PUBLISH對應的是PUBACK,與SUBSCRIBE或UNSUBSCRIBE對應的分別是SUBACK或UNSUBACK。針對MQTT5.0版本來說,QoS 2的PUBLISH對應的是包含原因碼128以上的PUBCOMP或PUBREC,而MQTT3.1.1來說,QoS 2的PUBLISH僅對應的是PUBCOMP。
- 與PUBLISH,SUBSCRIBE和UNSUBSCRIBE數據報文一起使用的數據報文標識符構成一次會話中客戶端和服務器的單個統一標識符集。報文標識符在任何時候都不能被多個命令使用。
- PUBACK,PUBREC或PUBREL數據報文必須包含與最初發送的PUBLISH數據報文相同的數據報文標識符
- 類似地,SUBACK和UNSUBACK必須包含在對應的SUBSCRIBE和UNSUBSCRIBE報文中使用的報文標識符
客戶端和服務端彼此獨立地分配報文標識符。因此,客戶端服務端組合使用相同的報文標識符可以實現併發的消息交換。
非規範評註
客戶端發送標識符爲0x1234的PUBLISH報文,它有可能會在收到那個報文的PUBACK之前,先收到服務端發送的另一個不同的但是報文標識符也爲0x1234的PUBLISH報文。
屬性(MQTT5.0)
CONNECT,CONNACK,PUBLISH,PUBACK,PUBREC,PUBREL,PUBCOMP,SUBSCRIBE,SUBACK,UNSUBSCRIBE,UNSUBACK,DISCONNECT和AUTH數據報文的可變報頭中的最後一個字段是一組屬性。 在CONNECT數據包中,有效載荷的遺願Properties字段中還有一組可選的Properties。
屬性字段由屬性長度和所有屬性組成。
屬性長度
屬性長度被編碼爲可變字節整數。屬性長度不包括用於編碼自身的字節,但包括屬性的長度。如果沒有屬性,則必須通過包含零的屬性長度來指示。
屬性
一個屬性由一個標識符定義,該標識符定義其用途和數據類型,後跟一個值。 標識符被編碼爲可變字節整數。 包含對它的數據報文類型無效的標識符或包含的值不是指定數據類型的控制數據報文是無效數據報文。 如果收到,服務端或客戶端使用包含原因碼0x81(無效報文)CONNACK或DISCONNECT報文進行錯誤處理。 具有不同標識符的屬性的順序沒有意義。
MQTT5屬性表
無法確定的翻譯,怕譯不準確,以括號原始英文給出,例如Server Reference
非規範評註
儘管屬性標識符被定義爲可變字節整數,但在MTQQ5.0的規範中,所有屬性標識符的長度均爲一個字節。
有效載荷
一些MQTT控制數據報文包含有效載荷作爲數據報文的最後部分。 在PUBLISH 報文中,是指應用消息。
黃色部分突出顯示的是MQTT5和MQTT3.1.1的不同
MQTT5新增
原因碼(MQTT5.0)
原因碼是一個無符號的單字節值,指示操作的結果。 原因代碼小於0x80表示操作成功完成。 成功的常規原因碼爲0。原因碼值爲0x80或更大表示失敗。
CONNACK,PUBACK,PUBREC,PUBREL,PUBCOMP,DISCONNECT和AUTH控制數據報文具有單個原因碼作爲變量頭的一部分。 SUBACK和UNSUBACK數據包在有效負載中包含一個或多個原因代碼的列表。
原因碼共享一組通用值,如下所示。
原因碼錶
非規範評註
對於原因碼0x91(正在使用的數據報文標識符),對此的響應是嘗試修復狀態,或者通過使用設置爲1的“Clean Start”標誌位進行連接來重置會話狀態,或者確定客戶端或服務器實現是否有缺陷。
總結
MQTT5增加了共享訂閱,屬性等內容,實現負載均衡。下一篇博文將會介紹MQTT控制報文的第一個報文,CONNECT報文。