物聯網之MQTT3.1.1和MQTT5協議 (2) CONNECT報文

前言

本篇博文針對MQTT控制報文進行闡述,將MQTT5和MQTT3.1.1的不同部分進行細分。

CONNECT –連接請求

客戶端與服務器建立網絡連接後,從客戶端發送至服務器的第一個數據報文必須爲CONNECT數據報文

客戶端只能通過網絡連接發送一次CONNECT數據報文。 服務器必須處理從客戶端發送的第二個CONNECT數據報文,作爲協議衝突,並斷開客戶端的連接

有效載荷包含一個或多個編碼字段。 它們爲客戶端指定唯一的客戶端標識符,“遺囑”主題,“遺囑消息”,“用戶名”和“密碼”。 除客戶端標識符外,所有標識符都是可選的,它們的存在是根據可變報頭中的標誌位確定的。

固定報頭

圖例 CONNECT報文的固定報頭

在這裏插入圖片描述

剩餘長度字段

剩餘長度等於可變報頭的長度加上有效載荷的長度。編碼方式爲可變字節整數。

可變報頭

CONNECT 報文的可變報頭按下列次序包含四個字段:協議名(Protocol Name),協議級別(Protocol Level),連接標誌(Connect Flags),保持連接(Keep Alive)。MQTT5.0版本則在此基礎加上屬性(Properties)字段

協議名

圖例 協議名字節構成
在這裏插入圖片描述

協議名稱是UTF-8編碼的字符串,代表協議名稱“ MQTT”。 字符串,其偏移量和長度將不會在MQTT規範的未來版本中更改。

如果協議名稱不正確,服務器可以斷開客戶端的連接,或者可以繼續按照其他規範處理CONNECT數據報文。 在後一種情況下,服務器不得根據MQTT的規範繼續處理CONNECT數據報文

針對MQTT5.0版本變化

支持多種協議的服務器使用協議名稱來確定數據是否爲MQTT。 協議名稱必須爲UTF-8字符串“ MQTT”。 如果服務器不希望接受CONNECT,並希望表明它是MQTT服務器,則可以發送原因碼爲0x84(不支持的協議版本)的CONNACK數據報文,然後必須關閉網絡連接

數據報文檢測工具,例如防火牆,可以使用協議名來識別MQTT流量。

協議級別(版本)

在這裏插入圖片描述

客戶端用8位的無符號值表示協議的修訂版本。對於3.1.1版協議,協議級別字段的值是4(0x04)。MQTT v5.0的協議版本字段爲5(0x05)。

支持多版本MQTT協議的服務端使用協議版本字段判定客戶端正使用的MQTT協議版本

針對MQTT3.1.1版本如果發現不支持的協議級別,服務端必須給發送一個返回碼爲0x01(不支持的協議級別)的CONNACK報文響應CONNECT報文,然後斷開客戶端的連接

針對MQTT5.0版本如果協議版本不是5且服務端不願意接受此CONNECT報文,可以發送包含原因碼0x84(不支持的協議版本)的CONNACK報文,然後必須關閉網絡連接

連接標誌

連接標誌字節包含一些用於指定MQTT連接行爲的參數。它還指出有效載荷中的字段是否存在。

圖例 連接標誌位

在這裏插入圖片描述

服務端必須驗證CONNECT控制報文的保留標誌位(第0位)是否爲0,如果不爲0說明是無效報文,並關閉連接

清理會話(MQTT3.3.1,Clean Session)

位置:連接標誌字節的第1位

這個二進制位指定了會話狀態的處理方式。

客戶端和服務器可以存儲會話狀態,以使可靠的消息傳遞能夠在一系列網絡連接中繼續進行。 該位用於控制會話狀態的生存期。

如果Clean Session設置爲0,則服務器務必根據當前會話的狀態(由客戶端標識符標識)恢復與客戶端的通信。 如果沒有與客戶端標識符相關的會話,則服務器必須創建一個新的會話。 客戶端和服務器斷開連接後,客戶端和服務器必須存儲會話。 在將Clean Session設置爲0的會話斷開連接後,服務器務必須將與斷開連接時客戶端具有的所有訂閱相匹配的其他QoS 1和QoS 2消息作爲會話狀態的一部分。 還可以存儲滿足相同條件的QoS 0消息。

客戶端中的會話狀態包括

  • 已發送到服務器但尚未完全確認的QoS 1和QoS 2消息。
  • 已從服務器收到但未完全確認的QoS 2消息。

服務器中的會話狀態包括:

  • 會話的存在,即使會話的其餘狀態爲空。
  • 客戶的訂閱。
  • 已發送到客戶端但尚未完全確認的QoS 1和QoS 2消息。
  • 等待傳輸到客戶端的QoS 1和QoS 2消息。
  • 已經從客戶端收到但尚未完全確認的QoS 2消息。
  • (可選)等待傳輸到客戶端的QoS 0消息。

保留消息不構成服務器中會話狀態的一部分,在會話結束時不得刪除它們

當Clean Session設置爲1時,客戶端和服務器無需自動處理狀態刪除。

爲了確保發生故障時狀態保持一致,客戶端應重複嘗試將Clean Session設置爲1進行連接,直到成功連接爲止。

通常,客戶端將始終使用設置爲0的CleanSession或設置爲1的CleanSession進行連接,並且不交替使用這兩值。選擇將取決於應用程序。使用Clean Session設置爲1的客戶端將不會收到舊的應用程序消息,並且必須在每次連接時重新訂閱其感興趣的任何主題。使用Clean Session設置爲0的客戶端將收到斷開連接時發佈的所有QoS 1或QoS 2消息。因此,爲確保斷開連接時不會丟失消息,請使用Clean Session設置爲0的QoS 1或QoS 2。

當客戶端將Clean Session設置爲0連接時,它要求服務器在斷開連接後保持其MQTT會話狀態。如果客戶端打算在以後的某個時間點重新連接到服務器,則客戶端應僅將Clean Session設置爲0進行連接。當客戶端確定該會話不再使用該客戶端時,應在Clean Session設置爲1的情況下進行最終連接,然後斷開連接。

新開始(MQTT5.0,Clean Start)

位置:連接標誌字節的第1位

該位指定連接是啓動新會話還是現有會話的延續。

如果在Clean Start設置爲1的情況下接收到CONNECT數據包,則客戶端和服務器務必丟棄任何現有會話並開始新的會話。 因此,如果“Clean Start”設置爲1,則CONNACK中的“Session Present”標誌始終設置爲0。

如果接收到的CONNECT數據包的Clean Start設置爲0,並且有一個與客戶端標識符相關聯的會話,則服務器必須根據現有會的狀態恢復與客戶端的通信。 如果接收到的CONNECT數據包的Clean Start設置爲0,並且沒有與客戶端標識符關聯的會話,則服務器必須創建一個新的會話

備註:把Clean Start標誌設置爲1且會話過期間隔設置爲0,等同於在MQTT v3.1.1中把清理會話(Clean Session)設置爲1。會話過期間隔時間設置是在屬性列表中存在。–qiwei.tan

遺囑標誌

位置:連接標誌字節的第2位

如果將Will Flag設置爲1

【MQTT3.1.1】

表示如果接受了連接請求,則遺囑(Will Message)消息必須被存儲在服務端並且與這個網絡連接關聯。除非網絡在收到DISCONNECT數據包後刪除了遺囑消息,否則必須在隨後關閉網絡連接時發佈遺囑消息

【MQTT 5.0】

表示遺囑消息必須已存儲在服務端與此客戶標識符相關的會話中。遺囑必須在網絡連接被關閉、遺囑延時間隔到期或者會話結束之後被髮布,除非服務端收到包含原因碼爲0x00(正常關閉)的DISCONNECT報文之後刪除了遺囑消息(Will Message),或者一個關於此客戶標識符的新的網絡連接在遺囑延時間隔(Will Delay Interval)超時之前被創建

備註:遺囑延時間隔是屬性的一種,在MQTT5.0中定義。

MQTT5.0標註

遺囑消息包含遺囑屬性,遺囑主題和遺囑載荷字段。

發佈遺囑消息的情況包括但不限於:

  • 服務器檢測到I / O錯誤或網絡故障。

  • 客戶端在保持活動時間內無法通信。

  • 客戶端沒有先發送DISCONNECT數據報文直接關閉網絡連接。【MQTT3.1.1】

    MQTT 5.0: 客戶端在沒有發送包含原因碼0x00(正常關閉)的DISCONNECT 報文的情況下關閉了網絡連接

  • 由於協議錯誤,服務器關閉了網絡連接。【MQTT3.1.1】

    MQTT 5.0: 服務端在沒有收到包含原因碼0x00(正常關閉)的DISCONNECT 報文的情況下關閉了網絡連接。與MQTT3.1.1關閉連接的情況不一樣,要先決條件

【MQTT3.1.1】

如果將Will Flag設置爲1,則服務器將使用Connect Flag中的Will QoS和Will Retain字段,並且有效負載中必須存在Will Topic和Will Message字段。一旦發佈了Will消息,或者服務器從客戶端接收到DISCONNECT數據包,則必須將遺囑消息從服務器中的已存儲會話狀態中刪除

如果將Will標誌設置爲0,則必須將連接標誌中的Will QoS和Will Retain字段設置爲零,並且在有效載荷中不得出現Will Topic和Will Message字段

如果將Will標誌設置爲0,則在此網絡連接結束時不得發佈遺囑消息

服務器應該及時發佈遺囑消息。在服務器關閉或失敗的情況下,服務器可以將Will Messages的發佈推遲到隨後的重啓。如果發生這種情況,則在服務器出現故障與發佈Will消息之間可能會有延遲。

【MQTT 5.0】

如果遺囑標誌(Will Flag)被設置爲1,遺囑屬性(Will Property)、遺囑主題(Will Topic)和遺囑載荷(Will Payload)字段必須存在於報文有效載荷中。一旦遺囑消息(Will Message)被髮布或者服務端收到來自於客戶端發送的包含原因碼爲0x00(正常關閉)的DISCONNECT報文,遺囑消息(Will Message)必須從服務端的會話中刪除

在網絡連接關閉且經過“ Will Delay Interval”之後或會話結束時,服務器應立即發佈“ 遺囑”消息。 在服務器關閉或失敗的情況下,服務器可以將“ 遺囑”消息的發佈推遲到隨後的重啓。 如果發生這種情況,則在服務器出現故障的時間與發佈“遺囑消息”之間可能會有延遲。

客戶端可以通過將“ Will Delay Interval”設置爲比“ Session Expiry Interval”更長,然後發送“ DISCONNECT”(原因碼爲0x04)(“與Will Message斷開連接”),以安排“ Will Message”通知會話已經到期。

遺囑QoS

位置:連接標誌的第4位和第3位。

用於指定發佈遺囑消息時要使用的QoS等級。

如果將Will Flag設置爲0,則Will QoS必須設置爲0(0x00)

如果將Will Flag設置爲1,則Will QoS的值可以爲0(0x00),1(0x01)或2(0x02)。而3(0x03)是格式錯誤的數據報文。

遺囑保留

位置:連接標誌的第5位

此位指定遺囑消息(Will Message)在發佈時是否會被保留。

  • 如果遺囑標誌被設置爲0,遺囑保留(Will Retain)標誌也必須設置爲0
  • 如果遺囑標誌被設置爲1
    • Will Retain設置爲0,則服務器必須將遺囑消息發佈爲非保留消息
    • Will Retain設置爲1,則服務器務必發佈Will消息作爲保留消息

用戶名標誌

位置:連接標誌字節的第7位

  • 如果用戶名(User Name)標誌被設置爲0,有效載荷中不能包含用戶名字段
  • 如果用戶名(User Name)標誌被設置爲1,有效載荷中必須包含用戶名字段
密碼標誌

位置:連接標誌的第6位。

  • 如果密碼標誌(Password Flag)被設置爲0,有效載荷中不能包含密碼字
  • 如果密碼(Password)標誌被設置爲1,有效載荷中必須包含密碼字段

【MQTT3.1.1】

如果用戶名標誌被設置爲0,密碼標誌也必須設置爲0

【MQTT 5.0】

允許在沒有用戶名的情況下發送密碼。這表明密碼除了作爲口令之外還可以有其他用途。

保活(Keep Alive)

在這裏插入圖片描述

保活(Keep Alive)是一個以秒爲單位的時間間隔,表示爲一個16位的字,它是指在客戶端傳輸完成一個控制報文的時刻到發送下一個報文的時刻,兩者之間允許空閒的最大時間間隔。客戶端負責保證控制報文發送的時間間隔不超過保活的值。如果沒有任何其它的控制報文可以發送,客戶端必鬚髮送一個PINGREQ報文

MQTT5 還額外要求Keep Alive不爲零時,發送一個PINGREQ報文

【MQTT5.0】如果服務端返回的CONNACK報文中包含服務端保活(Server Keep Alive)值,客戶端必須使用此值代替其發送的保活的值

不管保活的值是多少,客戶端任何時候都可以發送PINGREQ報文,並且使用PINGRESP報文判斷網絡和服務端的活動狀態。

  • 如果保持連接的值非零,並且服務端在1.5倍的保活時間內沒有收到客戶端的控制報文,它必須斷開客戶端的網絡連接,認爲網絡連接已斷開

  • 客戶端發送了PINGREQ報文之後,如果在合理的時間內仍沒有收到PINGRESP報文,它應該關閉到服務端的網絡連接。

  • 保活值爲零表示關閉保活功能。這意味着,服務端不需要因爲客戶端不活躍而斷開連接。注意:不管保持連接的值是多少,任何時候,只要服務端認爲客戶端是不活躍或無響應的,可以斷開客戶端的連接。在MQTT5.0增加:如果保活值爲零,客戶端不必按照任何特定的時間發送MQTT控制報文

服務端可能因爲其他原因斷開客戶端連接,比如服務端將要關閉服務。設置保活不保證客戶端將一直保持連接狀態。

保活實際值是由應用指定的,一般是幾分鐘。允許的最大值是18小時12分15秒。

CONNECT屬性(MQTT5.0)

屬性長度

CONNECT報文可變報頭中的屬性(Properties)長度被編碼爲變長字節整數。

會話過期間隔

17 (0x11)Byte,會話過期間隔(Session Expiry Interval)標識符。

跟隨其後的是用四字節整數表示的以秒爲單位的會話過期間隔(Session Expiry Interval)。包含多個會話過期間隔(Session Expiry Interval)將造成協議錯誤(Protocol Error)。

如果會話過期間隔(Session Expiry Interval)值未指定,則使用0。如果設置爲0或者未指定,會話將在網絡連接(Network Connection)關閉時結束。

如果會話過期間隔(Session Expiry Interval)爲0xFFFFFFFF (UINT_MAX),則會話永不過期。

如果網絡連接關閉時會話過期間隔(Session Expiry Interval)大於0,則客戶端與服務端必須存儲會話狀態

客戶端或服務端可能會因爲中斷運行導致會話時鐘某些時間未運行。這將導致會話的刪除被延遲。

當會話過期時,客戶端和服務端無需以原子操作的方式刪除會話狀態。

把新開始(Clean Start)標識設置爲1且會話過期間隔(Session Expiry Interval)設置爲0,等同於在MQTT v3.1.1中把清理會話(CleanSession)設置爲1。把新開始(Clean Start)標識設置爲0且不設置會話過期間隔(Session Expiry Interval),等同於在MQTT v3.1.1中把清理會話標誌設置爲0。

當希望只處理連接上服務端之後才發佈的消息,客戶端應該把新開始(Clean Start)設置爲1且會話過期間隔(Session Expiry Interval)設置爲0,這樣客戶端就不會收到它連接之前被服務端所發佈的消息,並且需要每次連接上服務端時重新訂閱其感興趣的主題。

某些客戶端使用的網絡可能只能提供斷斷續續的連接,這種客戶端可以使用較短的會話過期間隔(Session Expiry Interval)以便在網絡再次可用後重新連接到服務端時獲得持續的消息交付。如果客戶端不再重新連接,且允許會話過期,應用消息將會丟失。

某個客戶端設置較長的會話過期間隔(Session Expiry Interval)或設置會話不過期,即要求服務端爲其保持會話到其下一次連接上服務端之後。只有打算在一段時間之後將會重連服務端時,客戶端才應該設置較長的會話過期間隔(Session Expiry Interval)。當客戶端認定其將來不會使用本次會話時,應該在斷開時把會話過期間隔(Session Expiry Interval)設置爲0。

客戶端應當使用CONNACK報文中的會話存在(Session Present)來判定服務端是否存儲了其會話。

客戶端應當以服務端返回的會話存在(Session Present)標誌來判定會話是否已過期,而不是客戶端自己實現的會話過期狀態。如果客戶端自己實現會話過期狀態,則需要將會話應當被刪除的時間作爲會話狀態的一部分而存儲。

接收最大值

33 (0x21)Byte,接收最大值(Receive Maximum)標識符。

跟隨其後的是由雙字節整數表示的最大接收值。包含多個接收最大值或接收最大值爲0將造成協議錯誤(Protocol Error)。

客戶端使用此值限制客戶端願意同時處理的QoS等級1和QoS等級2的發佈消息最大數量。沒有機制可以限制服務端試圖發送的QoS爲0的發佈消息。

接收最大值只將被應用在當前網絡連接。如果沒有設置最大接收值,將使用默認值65535。

最大報文長度

39 (0x27)Byte,最大報文長度(Maximum Packet Size)標識符。

跟隨其後的是由四字節整數表示的客戶端願意接收的最大報文長度(Maximum Packet Size),如果沒有設置最大報文長度(Maximum Packet Size),則按照協議由固定報頭中的剩餘長度可編碼最大值和協議報頭對數據包的大小做限制。

包含多個最大報文長度(Maximum Packet Size)或者最大報文長度(Maximum Packet Size)值爲0將造成協議錯誤。

客戶端如果選擇了限制最大報文長度,應該爲最大報文長度設置一個合理的值。

最大報文長度是MQTT控制報文的總長度。客戶端使用最大報文長度通知服務端其所能處理的單個報文長度限制。

服務端不能發送超過最大報文長度(Maximum Packet Size)的報文給客戶端。收到長度超過限制的報文將導致協議錯誤,客戶端發送包含原因碼0x95(報文過大)的DISCONNECT報文給服務端。

當報文過大而不能發送時,服務端必須丟棄這些報文,然後當做應用消息發送已完成處理

共享訂閱的情況下,如果一條消息對於部分客戶端來說太長而不能發送,服務端可以選擇丟棄此消息或者把消息發送給剩餘能夠接收此消息的客戶端。

服務端可以把那些沒有發送就被丟棄的報文放在死信隊列(dead letter queue)上,或者執行其他診斷操作。

主題別名最大值

34 (0x22)Byte,主題別名最大值(Topic Alias Maximum)標識符。

跟隨其後的是用雙字節整數表示的主題別名最大值(Topic Alias Maximum)。包含多個主題別名最大值(Topic Alias Maximum)將造成協議錯誤(Protocol Error)。沒有設置主題別名最大值屬性的情況下,主題別名最大值默認爲零。

此值指示了客戶端能夠接收的來自服務端的主題別名(Topic Alias)最大數量。客戶端使用此值來限制本次連接可以擁有的主題別名的數量。服務端在一個PUBLISH報文中發送的主題別名不能超過客戶端設置的主題別名最大值(Topic Alias Maximum)。值爲零表示本次連接客戶端不接受任何主題別名(Topic Alias)。如果主題別名最大值(Topic Alias)沒有設置,或者設置爲零,則服務端不能向此客戶端發送任何主題別名(Topic Alias)

請求響應信息

25 (0x19)Byte,請求響應信息(Request Response Information)標識符。

跟隨其後的是用一個字節表示的0或1。包含多個請求響應信息(Request Response Information),或者請求響應信息(Request Response Information)的值既不爲0也不爲1會造成協議錯誤(Protocol Error)。如果沒有請求響應信息(Request Response Information),則請求響應默認值爲0。

客戶端使用此值向服務端請求CONNACK報文中的響應信息(Response Information)。值爲0,表示服務端不能返回響應信息。值爲1,表示服務端可以在CONNACK報文中返回響應信息。

即使客戶端請求響應信息(Response Information),服務端也可以選擇不發送響應信息(Response Information)。

請求問題信息

23 (0x17)Byte,請求問題信息(Request Problem Information)標識符。

跟隨其後的是用一個字節表示的0或1。包含多個請求問題信息(Request Problem Information),或者請求問題信息(Request Problem Information)的值既不爲0也不爲1會造成協議錯誤(Protocol Error)。如果沒有請求問題信息(Request Problem Information),則請求問題默認值爲1。

客戶端使用此值指示遇到錯誤時是否發送原因字符串(Reason String)或用戶屬性(User Properties)。

如果請求問題信息的值爲0,服務端可以選擇在CONNACK或DISCONNECT報文中返回原因字符串(Reason String)或用戶屬性(User Properties),但不能在除PUBLISH,CONNACK或DISCONNECT之外的報文中發送原因字符串(Reason String)或用戶屬性(User Properties)。如果此值爲0,並且在除PUBLISH,CONNACK或DISCONNECT之外的報文中收到了原因字符串(Reason String)或用戶屬性(User Properties),客戶端將發送一個包含原因碼0x82(協議錯誤)的DISCONNECT報文給服務端。

用戶屬性

38 (0x26)Byte,用戶屬性(User Property)標識符。

跟隨其後的是UTF-8字符串鍵值對。

用戶屬性(User Property)可以出現多次,表示多個名字/值對。相同的名字可以出現多次。

CONNECT報文中的用戶屬性可以被用來發送客戶端到服務端的連接相關的屬性。

認證方法

21 (0x15)Byte,認證方法(Authentication Method)標識符。

跟隨其後的是一個UTF-8編碼的字符串,包含了擴展認證的認證方法(Authentication Method)名稱。包含多個認證方法將造成協議錯誤(協議錯誤)。

如果沒有認證方法,則不進行擴展驗證。

如果客戶端在CONNECT報文中設置了認證方法,則客戶端在收到CONNACK報文之前不能發送除AUTH或DISCONNECT之外的報文

認證數據

22 (0x16)Byte,認證數據(Authentication Data)標識符。

跟隨其後的是二進制的認證數據。沒有認證方法卻包含了認證數據(Authentication Data),或者包含多個認證數據(Authentication Data)將造成協議錯誤(Protocol Error)。

認證數據的內容由認證方法定義。

可變報頭非規範示例

在這裏插入圖片描述

在這裏插入圖片描述

有效載荷

CONNECT報文的有效載荷(payload)包含由可變報頭(Variable Header)中的標誌確定的一個或多個以長度爲前綴的字段

【MQTT3.1.1】

如果包含的話,必須按這個順序出現:客戶端標識符,遺囑主題,遺囑消息,用戶名,密碼

【MQTT 5.0】

必須按照客戶標識符(Client Identifier)、遺囑屬性(Will Properties)、遺囑主題(Will Topic)、遺囑載荷(Will Payload)、用戶名(User Name)、密碼(Password)的順序出現

客戶端標識符(ClientID)

服務端使用客戶端標識符 (Client Id) 識別客戶端。連接服務端的每個客戶端都有唯一的客戶端標識符(Client Id)。客戶端和服務端都必須使用Client Id識別兩者之間的MQTT會話相關的狀態

  • 必須存在ClientID,並且它是CONNECT數據包有效載荷中的第一個字段

  • ClientID必須是UTF-8編碼的字符串

  • 服務器務必允許ClientID的長度介於1到23個UTF-8編碼字節之間,並且僅包含字符

    0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ

  • 服務器可以允許包含超過23個編碼字節的ClientID。 服務器可以允許ClientID包含上面所列列表中未包含的字符

  • 服務器可以允許客戶端提供一個長度爲零字節的客戶端ID,但是如果這樣做,則服務器必須將其作爲一種特殊情況,併爲該客戶端分配一個唯一的客戶端ID 。 然後,它必須像客戶端已經提供了唯一的ClientID一樣處理CONNECT包,並且必須在CONNACK包中返回已分配的客戶端標識符【MQTT3.1.1】中如果客戶端提供了一個零字節的客戶端標識符,它必須同時將清理會話標誌設置爲1。如果客戶端提供的ClientId爲零字節且清理會話標誌爲0,服務端必鬚髮送返回碼爲0x02(表示標識符不合格)的CONNACK報文響應客戶端的CONNECT報文,然後關閉網絡連接

  • 如果服務器拒絕了ClientID,則它可以使用原因代碼0x85(客戶端標識符無效)以CONNACK響應CONNECT數據包,然後必須關閉網絡連接。【MQTT5】

客戶端實現可以提供一種方便的方法來生成隨機的ClientID。 使用此方法的客戶應注意避免創建壽命長的孤立會話。在MQTT3.1.1時,當清理會話標誌被設置爲0時應該主動放棄使用這種方法。

遺囑屬性(MQTT 5)

如果遺囑標誌(Will Flag)被設置爲1,有效載荷的下一個字段是遺囑屬性(Will Properties)。遺囑屬性字段定義了遺囑消息(Will Message)將何時被髮布,以及被髮布時的應用消息(Application Message)屬性。遺囑屬性包括屬性長度和屬性。

屬性長度

遺囑屬性(Will Properties)中的屬性長度被編碼爲可變長字節整數。

遺囑延時間隔

24 (0x18)Byte,遺囑延時間隔(Will Delay Interval)標識符。

跟隨其後的是由四字節整數表示的以秒爲單位的遺囑延時間隔(Will Delay Interval)。包含多個遺囑延時間隔將造成協議錯誤(Protocol Error)。如果沒有設置遺囑延時間隔,遺囑延時間隔默認值將爲0,即不用延時發佈遺囑消息(Will Message)。

服務端將在遺囑延時間隔(Will Delay Interval)到期或者會話(Session)結束時發佈客戶端的遺囑消息(Will Message),取決於兩者誰先發生。如果某個會話在遺囑延時間隔到期之前創建了新的網絡連接,則服務端不能發送遺囑消息

遺囑延時間隔的一個用途是避免在頻繁的網絡連接臨時斷開時發佈遺囑消息,因爲客戶端往往會很快重新連上網絡並繼續之前的會話。

如果某個連接到服務端的網絡連接使用已存在的客戶標識符,此已存在的網絡連接的遺囑消息將會被髮布,除非新的網絡連接設置了新開始(Clean Start)爲0並且遺囑延時大於0。如果遺囑延時爲0,遺囑消息將在網絡連接斷開時發佈。如果新開始爲1,遺囑消息也將被髮布,因爲此會話已結束。

載荷格式指示

1 (0x01)Byte,載荷格式指示(Payload Format Indicator)標識符。

跟隨載荷格式指示(Payload Format Indicator )之後的可能是:

  • 0 (0x00),表示遺囑消息(Will Message)是未指定的字節,等同於不發送載荷格式指示。
  • 1 (0x01),表示遺囑消息(Will Message)是UTF-8編碼的字符數據。載荷中的UTF-8數據必須按照Unicode規範和RFC 3629中的申明進行編碼。

包含多個載荷格式指示(Payload Format Indicator)將造成協議錯誤(Protocol Error)。服務端可以按照格式指示對遺囑消息(Will Message)進行驗證,如果驗證失敗發送一條包含原因碼0x99(載荷格式無效)的CONNACK報文。

消息過期間隔

2 (0x02)Byte,消息過期間隔(Message Expiry Interval)標識符。

跟隨其後的是表示消息過期間隔(Message Expiry Interval)的四字節整數。包含多個消息過期間隔將導致協議錯誤(Protocol Error)。

如果設定了消息過期間隔(Message Expiry Interval),四字節整數描述了遺囑消息的生命週期(秒),並在服務端發佈遺囑消息時被當做發佈過期間隔(Publication Expiry Interval)。

如果沒有設定消息過期間隔,服務端發佈遺囑消息時將不發送消息過期間隔(Message Expiry Interval)。

內容類型

3(0x03)Byte,內容類型的標識符。

跟隨其後的是一個以UTF-8格式編碼的字符串,用來描述遺囑消息(Will Message)的內容。包含多個內容類型(Content Type)將造成協議錯誤(Protocol Error)。內容類型的值由發送應用程序和接收應用程序確定。

響應主題

8 (0x08)Byte,響應主題(Response Topic)標識符。 跟隨其後的是一個以UTF-8格式編碼的字符串,用來表示響應消息的主題名(Topic Name)。包含多個響應主題(Response Topic)將造成協議錯誤。響應主題的存在將遺囑消息(Will Message)標識爲一個請求報文。

對比數據

9 (0x09)Byte,對比數據(Correlation Data)標識符。

跟隨其後的是二進制數據。對比數據被請求消息發送端在收到響應消息時用來標識相應的請求。包含多個對比數據將造成協議錯誤(Protocol Error)。如果沒有設置對比數據,則請求方(Requester)不需要任何對比數據。

對比數據只對請求消息(Request Message)的發送端和響應消息(Response Message)的接收端有意義。

用戶屬性

38 (0x26)Byte,用戶屬性(User Property)標識符。

跟隨其後的是一個UTF-8字符串對。用戶屬性(User Property)可以出現多次,表示多個名字/值對。相同的名字可以出現多次。

服務端在發佈遺囑消息(Will Message)時必須維護用戶屬性(User Properties)的順序

此屬性旨在提供一種傳遞應用層名稱-值標籤的方法,其含義和解釋僅由負責發送和接收它們的應用程序所有。

遺囑主題

如果遺囑標誌被設置爲1,有效載荷的下一個字段是遺囑主題(Will Topic)。遺囑主題必須是UTF-8編碼字符串

遺囑消息(MQTT3.1.1)

如果遺囑標誌被設置爲1,有效載荷的下一個字段是遺囑消息。遺囑消息定義了將被髮布到遺囑主題的應用消息。這個字段由一個兩字節的長度和遺囑消息的有效載荷組成,表示爲零字節或多個字節序列。長度給出了跟在後面的數據的字節數,不包含長度字段本身佔用的兩個字節。
遺囑消息被髮布到遺囑主題時,它的有效載荷只包含這個字段的數據部分,不包含開頭的兩個長度字節。

遺囑載荷(MQTT 5)

如果遺囑標誌(Will Flag)被設置爲1,遺囑載荷(Will Payload)爲載荷中下一個字段。遺囑載荷定義了將要發佈到遺囑主題(Will Topic)的應用消息載荷。

此字段爲二進制數據。

用戶名

如果用戶名標誌(User Name Flag)被設置爲1,有效載荷的下一個字段就是它。用戶名必須是UTF-8編碼字符串。服務端可以將它用於身份驗證和授權。

密碼

如果密碼標誌(Password Flag)被設置爲1,密碼(Password)爲載荷中下一個字段。密碼字段是二進制數據,儘管被稱爲密碼,但可以被用來承載任何認證信息。

密碼字段包含一個兩字節的長度字段,長度表示二進制數據的字節數(不包含長度字段本身佔用的兩個字節),後面跟着0到65535字節的二進制數據。

在這裏插入圖片描述

關於密碼字節的示例在MQTT5.0並沒有像MQTT3.1.1顯示出來

CONNECT行爲

該節部分對應MQTT3.1.1所述的響應。

注意:服務器可以在同一個TCP端口或其他網絡端點上支持多種協議(包括本協議的早期版本)。

如果服務器確定協議是MQTT,那麼它按照下面的方法驗證連接請求。

  1. 網絡連接建立後,如果服務端在合理的時間內沒有收到CONNECT報文,服務端應該關閉這個連接。
  2. 服務端必須驗證CONNECT報文,如果報文不符合規範,MQTT3.1.1則要求服務端不發送CONNACK報文直接關閉網絡連接,MQTT5則表示服務端可以在關閉網絡連接之前發送包含0x80及以上原因碼的CONNACK報文,但也須關閉網絡連接
  3. 服務端可以檢查CONNECT報文的內容是不是滿足任何進一步的限制,可以執行身份驗證和授權檢查【在MQTT5中強調應該執行而非可以執行】。如果任何一項檢查沒通過,在MQTT3.1.1則應該發送一個適當的、返回碼非零的CONNACK響應,並且必須關閉這個網絡連接,而在MQTT5中,在關閉網絡連接之前服務端可以發送一個合適的包含如0x80及以上原因碼的CONNACK報文

如果驗證成功,服務端會執行下列步驟。

  1. 如果ClientId表明客戶端已經連接到這個服務端,那麼服務端必須斷開原有的客戶端連接,MQTT5則除了斷開原有網絡連接外,還向原有的客戶端發送一個包含原因碼爲0x8E(會話被接管)的DISCONNECT報文。如果原有客戶端存在遺囑消息(Will Message),併發布遺囑消息

    如果原有網絡連接包含遺囑消息,且遺囑延時間隔爲0,則遺囑消息會在此網絡連接被關閉時發送。如果原有網絡連接會話過期間隔爲0,或者新網絡連接新開始標誌設置爲1且原有網絡連接包含遺囑消息,則遺囑消息會被髮送,因爲原有會話已結束。

  2. 在MQTT3.1.1中,服務端必須執行清理會話的過程,而MQTT5.0則是對clean start標誌進行處理

  3. MQTT3.1.1中服務端必鬚髮送返回碼爲零的CONNACK報文作爲CONNECT報文的確認響應 ,而MQTT5.0中則是必須使用包含原因碼爲0x00(成功)的CONNACK報文對客戶端的CONNECT報文進行確認

    如果服務端被用來處理商業關鍵數據,推薦對網絡連接進行認證和授權。如果認證和授權成功,服務端可通過發送包含原因碼爲0x00(成功)的CONNACK報文進行響應,否則建議服務端根本不要發送CONNACK報文,因爲這是一種潛在的對MQTT服務端的攻擊,可以被用來進行拒絕服務因爲這是一種潛在的對MQTT服務端的攻擊,可以被用來進行拒絕服務

  4. 開始消息分發和保活狀態監視。

允許客戶端在發送CONNECT報文之後立即發送其它的控制報文;客戶端不需要等待服務端的CONNACK報文。如果服務端拒絕了CONNECT,MQTT3.1.1版本中,它不能處理客戶端在CONNECT報文之後發送的任何數據。在MQTT5版本中,則不能處理客戶端在CONNECT報文之後發送的任何除AUTH以外的報文

客戶端通常會等待一個CONNACK報文。然而客戶端有權在收到CONNACK之前發送控制報文,由於不需要維持連接狀態,這可以簡化客戶端的實現。【MQTT3.1.1】

客戶端通常會等待CONNACK報文。然而,如果在收到CONNACK報文之前就自由的發送其它QTT控制報文將會簡化客戶端的實現,因爲它不必監督連接的狀態。如果連接被拒絕了,客戶端在接收CONNACK報文之前發送的任何數據將不會被服務端所處理。【MQTT 5】

選擇在收到CONNACK報文之前就發送MQTT控制報文的客戶端將不知道服務端所存在的約束以及會話是否被使用。【MQTT 5】

服務端在對某個客戶端完成認證之前,可以選擇限制讀取該客戶端的網絡數據或者關閉該客戶端的網絡連接。這是一種避免拒絕服務攻擊的方法。

結語

CONNECT控制報文屬於第一個MQTT控制報文,沒有發送該報文,服務端一般需要拒絕連接,相應的broker如果沒有處理這個邏輯,可能會拋出異常。另外,CONNECT發送成功後,一般需要服務端返回一個響應的報文進行響應,下一篇則是說明確認連接請求的報文,即CONNACK報文。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章