共用方式為


IoT 中樞 MQTT 5 支援 (已淘汰)

版本:2.0 api-version:2020-10-01-preview

本文件定義透過 MQTT 5.0 版通訊協定的 IoT 中樞資料平面 API。 如需此 API 的完整定義,請參閱 API 參考

注意

IoT 中樞 中的 MQTT 5 支援已被取代,IoT 中樞 對 MQTT 的功能支援有限。 如果您的解決方案需要 MQTT v3.1.1 或 v5 支援,建議使用 Azure 事件方格中的 MQTT 支援。 如需詳細資訊,請參閱 比較 IoT 中樞和事件方格中的 MQTT 支援

必要條件

  • 建立已啟用預覽模式的全新IoT中樞。 MQTT 5 僅適用於預覽模式,您無法將現有的 IoT 中樞切換為預覽模式。 如需詳細資訊,請參閱 啟用預覽模式
  • MQTT 5 規格先前知識。

支援層級和限制

IoT 中樞的 MQTT 5 支援目前處於預覽狀態,且有下列限制 (除非另有明確註明,否則會透過 CONNACK 屬性與用戶端通訊):

  • 尚不支援官方 Azure IoT 裝置 SDK
  • 不支援訂閱識別碼。
  • 不支援共用訂閱。
  • 不支援 RETAIN
  • Maximum QoS1
  • Maximum Packet Size256 KiB (各項作業可能有其他限制)。
  • 不支援指派的用戶端識別碼。
  • Keep Alive 限制為 19 min (活躍度檢查的最大延遲:28.5 min)。
  • Topic Alias Maximum10
  • 不支援 Response Information;即使 CONNECT 包含 Request Response Information 屬性,CONNACK 也不會傳回 Response Information 屬性。
  • Receive Maximum (用戶端-伺服器方向) QoS: 1 允許的未處理且未經通知 PUBLISH 封包數目上限為 16
  • 單一用戶端不得擁有超過 50 個訂閱, 如果客戶端達到訂用帳戶限制, SUBACK 則會傳 0x97 回訂用帳戶的 [配額超過] 原因碼。

連線生命週期

連線

若要使用此 API 將用戶端連線到 IoT 中樞,請根據 MQTT 5 規格建立連線。 用戶端必須在 TLS 交握成功後 30 秒內傳送 CONNECT 封包,否則伺服器會關閉連線。 以下是 CONNECT 封包的範例:

-> CONNECT
    Protocol_Version: 5
    Clean_Start: 0
    Client_Id: D1
    Authentication_Method: SAS
    Authentication_Data: {SAS bytes}
    api-version: 2020-10-10
    host: abc.azure-devices.net
    sas-at: 1600987795320
    sas-expiry: 1600987195320
    client-agent: artisan;Linux
  • Authentication Method 為必要屬性,用於識別使用的驗證方法。 如需驗證方法的詳細資訊,請參閱驗證
  • Authentication Data 屬性處理取決於 Authentication Method。 若 Authentication Method 設定為 SAS,則 Authentication Data 為必要項目,且必須包含有效的簽章。 如需驗證資料的詳細資訊,請參閱驗證
  • api-version 為必要屬性,且必須設定為此規格標頭中提供的 API 版本值,才能套用此規格。
  • host 屬性用於決定租用戶的主機名稱。 除非在 TLS 交握期間 Client Hello 記錄中有 SNI 延伸模組,否則這是必要的屬性
  • sas-at 用於定義連線時間。
  • sas-expiry 用於定義所提供 SAS 的到期時間。
  • client-agent 會選擇性傳達建立連線之用戶端的相關資訊。

注意

Authentication Method 與其他大寫名稱規格中的屬性皆為 MQTT 5 中的第一級屬性,如 MQTT 5 規格的詳細資料所述。 api-version 與其他以破折號相連的屬性為 IoT 中樞 API 特定的使用者屬性。

IoT 中樞完成驗證並擷取用於支援連線的資料後,便會以 CONNACK 封包回應。 若成功建立連線,CONNACK 會如下所示:

<- CONNACK
    Session_Present: 1
    Reason_Code: 0x00
    Session_Expiry_Interval: 0xFFFFFFFF # included only if CONNECT specified value less than 0xFFFFFFFF and more than 0x00
    Receive_Maximum: 16
    Maximum_QoS: 1
    Retain_Available: 0
    Maximum_Packet_Size: 262144
    Topic_Alias_Maximum: 10
    Subscription_Identifiers_Available: 0
    Shared_Subscriptions_Available: 0
    Server_Keep_Alive: 1140 # included only if client did not specify Keep Alive or if it specified a bigger value

這些 CONNACK 封包屬性遵循 MQTT 5 規格 (英文), 可顯示 IoT 中樞的功能。

驗證

CONNECT 用戶端上的 Authentication Method 屬性用於定義對此連線使用的驗證種類:

  • SAS:共用存取簽章透過 CONNECTAuthentication Data 屬性提供。
  • X509:用戶端仰賴用戶端憑證驗證。

如果驗證方法不符合用戶端在 IoT 中樞設定的方法,驗證就會失敗。

注意

此 API 必須在 CONNECT 封包中設定 Authentication Method 屬性。 若未提供 Authentication Method 屬性,連線便會失敗,並顯示 Bad Request 回應。

不支援舊版 API 中所用的使用者名稱/密碼驗證。

SAS

使用 SAS 型驗證時,用戶端必須提供連線內容的簽章。 該簽章可證明 MQTT 連線的真確性。 該簽章必須以 IoT 中樞中用戶端設定中的兩個驗證金鑰之一為基礎。 或者,該簽章必須以共用存取原則的兩個共用存取金鑰之一為基礎。

簽署的字串必須採用下列格式:

{host name}\n
{Client Id}\n
{sas-policy}\n
{sas-at}\n
{sas-expiry}\n
  • host name 衍生自 SNI 延伸模組 (用戶端在 TLS 交握期間於 Client Hello 記錄中提供) 或 CONNECT 封包中的 host 使用者屬性。
  • Client IdCONNECT 封包中的用戶端識別碼。
  • sas-policy:如果存在,可定義用於驗證的 IoT 中樞存取原則, 會編碼為 CONNECT 封包上的使用者屬性。 選擇性:省略即代表將改用裝置登錄中的驗證設定。
  • sas-at:如果存在,用於指定連線時間-目前時間, 會編碼為 CONNECT 封包上的 time 種類使用者屬性。
  • sas-expiry 用於定義驗證的到期時間, 是 CONNECT 封包上的 time 類型使用者屬性。 此屬性是必要項。

若省略選擇性參數,則簽署的字串中「必須」改用空字串。

系統會根據其中一部裝置的對稱金鑰使用 HMAC-SHA256 來雜湊字串, 然後將雜湊值設為 Authentication Data 屬性的值。

X509

若將 Authentication Method 屬性設為 X509,IoT 中樞會根據提供的用戶端憑證驗證連線。

重新驗證

如果您選用 SAS 型驗證,建議使用短期驗證權杖。 若要讓連線保持為已驗證狀態,避免因到期而中斷連線,用戶端必須使用 Reason Code: 0x19 (重新驗證) 來傳送 AUTH 封包以重新驗證 :

-> AUTH
    Reason_Code: 0x19
    Authentication_Method: SAS
    Authentication_Data: {SAS bytes}
    sas-at: {current time}
    sas-expiry: {SAS expiry time}

規則:

  • Authentication Method 必須與最初驗證使用的方法相同
  • 如果連線起初根據共用存取原則使用 SAS 進行驗證,則重新驗證時使用的簽章必須以相同原則為依據。

重新驗證成功後,IoT 中樞會傳送 AUTH 封包與 Reason Code: 0x00 (成功); 若重新驗證失敗,則 IoT 中樞會傳送 DISCONNECT 封包與 Reason Code: 0x87 (未獲授權),並關閉連線。

中斷連線

伺服器可能會因為下列幾個原因而中斷用戶端連線,包括:

  • 用戶端錯誤的方式,無法直接回應負面認可(或回應)
  • 伺服器無法讓連線的狀態保持在最新狀態,
  • 另一個用戶端會連線到相同的身分識別。

伺服器可能會以 MQTT 5.0 規格中定義的任何原因代碼中斷連線。 以下列出幾個需注意的代碼:

  • 135 (未授權)重新驗證失敗時,目前的 SAS 令牌會過期,或裝置的認證變更。
  • 142 (工作階段被接管) 有人使用相同用戶端身分識別開啟連線。
  • 159 當IoT中樞的連線速率超過限制時,即已超過連線速率。
  • 131 (實作特定錯誤) 用於此 API 中定義的任何自訂錯誤。 statusreason 屬性會用於傳達與連線中斷原因相關的詳細資料 (詳情請參閱回應)。

Operations

此 API 中所有功能都以作業表示。 以下是傳送遙測作業的範例:

-> PUBLISH
    QoS: 1
    Packet_Id: 3
    Topic: $iothub/telemetry
    Payload: Hello

<- PUBACK
    Packet_Id: 3
    Reason_Code: 0

如需此 API 中作業的完整規格,請參閱 IoT 中樞資料平面 MQTT 5 API 參考

注意

此規格中所有範例都是以用戶端的觀點顯示, -> 符號表示用戶端傳送封包,<- 符號則代表接收。

訊息主題和訂閱

在此 API 中,作業訊息使用的主題皆以 $iothub/ 開頭。 MQTT 訊息代理程式的語意不適用於這些作業 (詳情請參閱「以 $ 開頭的主題」)。 不支援以 $iothub/ 開頭但未於此 API 中定義的主題:

  • 將訊息傳送至未定義的主題會得到 Not Found 回應 (詳情請參閱回應),
  • 訂閱未定義的主題會得到 SUBACKReason Code: 0x8F (主題篩選不正確)。

主題名稱和屬性名稱會區分大小寫,且必須完全相符。 例如,系統支援 $iothub/telemetry,但不支援 $iothub/telemetry/

注意

不支援在訂閱中於 $iothub/.. 後方使用萬用字元。 換句話說,用戶端無法訂閱 $iothub/+$iothub/#, 若嘗試這樣做會傳回 SUBACKReason Code: 0xA2 (不支援萬用字元訂閱)。 僅支援單一區段萬用字元 (+),而非有萬用字元之作業主題名稱中的路徑參數。

互動類型

此 API 中所有作業都是以下列兩種互動類型的其中一個為依據:

  • 具有選擇性通知的訊息 (MessageAck)
  • 要求-回應 (ReqRep)

作業也會因方向 (取決於交換中的初始訊息方向) 而有所不同:

  • 用戶端至伺服器 (c2s)
  • 伺服器至用戶端 (s2c)

例如,傳送遙測是「具有通知的訊息」類型的用戶端至伺服器作業,而處理直接方法是「要求-回應」類型的伺服器至用戶端作業。

訊息通知互動

具有選擇性通知的訊息 (MessageAck) 互動會以交換 MQTT 中的 PUBLISHPUBACK 封包表示。 通知是選擇性的,而且傳送者可以選擇不透過 傳送 PUBLISH 封包 QoS: 0來要求。

注意

若因用戶端所宣告的 Maximum Packet Size 導致必須截斷 PUBACK 封包中的屬性,則 IoT 中樞會在限制範圍內盡可能保留使用者屬性。 比起後列出的使用者屬性,先列出的屬性更有機會傳送出去;Reason String 屬性的優先順序最低。

簡易 MessageAck 互動的範例

訊息:

PUBLISH
    QoS: 1
    Packet_Id: 34
    Topic: $iothub/{request.path}
    Payload: <any>

通知 (成功) :

PUBACK
    Packet_Id: 34
    Reason_Code: 0

要求-回應互動

在要求-回應 (ReqRep) 互動中,要求和回應都會使用 QoS: 0 轉譯成 PUBLISH 封包。

要求和回應都必須設定 Correlation Data 屬性,用於比對回應封包與要求封包。

此 API 會對所有 ReqRep 作業使用單一回應主題 $iothub/responses。 為用戶端至伺服器作業訂閱/取消訂閱此主題並非必要做法,因為伺服器一律會假設訂閱所有用戶端。

簡易 ReqRep 互動的範例

要求:

PUBLISH
    QoS: 0
    Topic: $iothub/{request.path}
    Correlation_Data: 0x01 0xFA
    Payload: ...

回應 (成功):

PUBLISH
    QoS: 0
    Topic: $iothub/responses
    Correlation_Data: 0x01 0xFA
    Payload: ...

ReqRep 互動不支援將含有 QoS: 1PUBLISH 封包當作要求或回應訊息, 若傳送 PUBLISH 要求會得到 Bad Request 回應。

屬性 Correlation Data 中支援的長度上限為 16 個位元組。 如果將 PUBLISH 封包上的 Correlation Data 屬性設為超過 16 個位元組的值,則 IoT 中樞會傳送 DISCONNECTBad Request 結果,並關閉連線。 此行為僅適用於在這個 API 內交換的封包。

注意

相互關聯資料是任意位元組序列,例如不能保證為 UTF-8 字串。

ReqRep 使用預先定義的主題進行回應;要求 PUBLISH 封包中的回應主題屬性 (若由傳送者設定) 會受到忽略。

IoT 中樞會自動為所有用戶端至伺服器 ReqRep 作業訂閱回應主題用戶端。 即使用戶端明確取消訂閱回應主題,IoT 中樞依然會自動恢復訂閱。 針對伺服器至用戶端的 ReqRep 互動,裝置仍需訂閱。

郵件內容

(由系統或使用者定義的) 作業屬性在 MQTT 5 中以封包屬性表示。

使用者屬性名稱會區分大小寫,且必須完全符合定義的拼字, 例如,系統支援 trace-id,但不支援 Trace-ID

要求若具有不包含在規格中的使用者屬性,且沒有前置詞 @,即會發生錯誤。

系統屬性會編碼為第一級屬性 (例如 Content Type) 或使用者屬性。 規格提供完整的支援系統屬性清單, 除非規格中明確表示提供支援,否則所有第一級屬性都會受到忽略。

使用者定義屬性如獲准使用,其名稱必須遵循 @{property name} 格式。 使用者定義的屬性只支援有效的 UTF-8 字串值, 例如,值為 15MyProperty1 屬性必須編碼為名稱是 @MyProperty 且值是 15 的使用者屬性。

如果 IoT 中樞無法辨識使用者屬性,則視為發生錯誤,IoT 中樞會以 PUBACKReason Code: 0x83 (實作特定錯誤) 和 status: 0100 (不正確的要求) 回應。 若未要求通知 (QoS:0),即會傳回有相同錯誤的 DISCONNECT 封包並終止連線。

除了 string 外,此 API 定義了下列資料類型:

  • time:自 1970-01-01T00:00:00.000Z 開始計算的毫秒數, 例如 1600987195320 是指 2020-09-24T22:39:55.320Z
  • u32:不帶正負號的 32 位元整數值
  • u64:不帶正負號的 64 位元整數值
  • i32:帶正負號的 32 位元整數值

回應

互動可能得到 SuccessBad RequestNot Found 等多種結果, 結果會依 status 使用者屬性進行區分。 PUBACK 封包中的 Reason Code (MessageAck 互動) 在意義上會盡可能符合 status

注意

若用戶端在 CONNECT 封包中指定 Request Problem Information: 0,那麼系統不會為了遵循 MQTT 5 規格而透過 PUBACK 封包傳送任何使用者屬性 (包含 status 屬性)。 在這種情況下,用戶端仍可仰賴 Reason Code 判斷通知為正值還是負值。

每個互動都有預設值 (或成功), Reason Code0status 屬性為「未設定」, 否則:

  • 針對 MessageAck 互動,PUBACK 會得到 0x0 (成功) 以外的 Reason Code。 或許會有 status 可進一步釐清結果。
  • 針對 ReqRep 互動,回應 PUBLISH 會得到 status 屬性集。
  • 由於無法直接透過 QoS: 0 回應 MessageAck 互動,系統會改為傳送 DISCONNECT 封包與回應資訊,然後中斷連線。

範例:

不正確的要求 (MessageAck):

PUBACK
    Reason_Code: 131
    status: 0100
    reason: Unknown property `test`

未獲授權 (MessageAck):

PUBACK
    Reason_Code: 135
    status: 0101

未獲授權 (ReqRep):

PUBLISH
    QoS: 0
    Topic: $iothub/responses
    Correlation_Data: ...
    status: 0101

如有需要,IoT 中樞會設定下列使用者屬性:

  • status:IoT 中樞的作業狀態擴充程式碼。 此程式碼可用於區分結果。
  • trace-id:作業的追蹤識別碼。IoT 中樞或許會針對內部調查可能使用的作業保留更多診斷。
  • reason:人類看得懂的訊息,針對作業為何最終處於 status 屬性所指出的狀態提供詳細資訊。

注意

如果用戶端將 CONNECT 封包中的 Maximum Packet Size 屬性設定為非常小的值,有些使用者屬性可能無法符合該設定,也不會出現在封包中。

reason 僅供人類參考,不應於用戶端邏輯使用。 此 API 允許隨時變更訊息,不會出現警告或變更版本。

如果用戶端在 CONNECT 封包中傳送 RequestProblemInformation: 0,根據 MQTT 5 規格 (英文) 通知中不會包含使用者屬性。

狀態碼

status 屬性帶有作業的狀態碼, 且已針對機器讀取效率進行最佳化, 由編碼為十六進位、雙位元組、不帶正負號的整數字串組成,例如 0501。 程式碼結構 (點陣圖):

7 6 5 4 3 2 1 0 | 7 6 5 4 3 2 1 0
0 0 0 0 0 R T T | C C C C C C C C

第一個位元組用於旗標:

  • 位元 0 和 1 表示結果類型:
    • 00:成功
    • 01:用戶端錯誤
    • 10:伺服器錯誤
  • 位元 2:1 表示錯誤可重試
  • 會保留位元 3 到 7,且必須設定為 0

第二個位元組包含實際的不同回應碼。 具有不同旗標的錯誤碼可以有相同的第二個位元組值, 例如 0001010102010301 錯誤碼的意義都不同。

例如,Too Many Requests 是用戶端,自帶代碼 1 的可重試錯誤, 其值為 0000 0101 0000 00010x0501

用戶端可使用類型位元來識別作業是否成功結束, 也能使用可重試位元決定重試作業是否合理。

建議

工作階段管理

CONNACK 封包具有 Session Present 屬性,能指出伺服器是否已還原先前建立的會話。 請使用這個屬性了解要訂閱主題還是略過稍早已完成的訂閱。

如要仰賴 Session Present,用戶端必須追蹤其訂閱 (也就是傳送 SUBSCRIBE 封包,並接收 SUBACK 與成功的原因代碼),或確認在單一 SUBSCRIBE/SUBACK 交換中訂閱所有主題, 否則用戶端若傳送兩個 SUBSCRIBE 封包,而伺服器只成功處理其中一個封包,那麼伺服器會在 CONNACK 中傳達 Session Present: 1,且只接受部分用戶端訂閱。

為了避免發生舊版用戶端未訂閱所有主題的情況,建議在用戶端行為變更時無條件訂閱 (例如在韌體更新時執行)。 此外,為了確保不會留下過時的訂閱 (從允許訂閱數上限取得),請明確取消不再使用的訂閱。

批次處理

傳送訊息批次不需使用特殊格式。 若要減少 TLS 和網路中會耗用大量資源的作業所帶來的額外負荷,請先將封包 (PUBLISHPUBACKSUBSCRIBE 等) 組合在一起,再提供給基礎 TLS/TCP 堆疊。 此外,用戶端在「批次」中更容易設定主題別名:

  • 將完整主題名稱放入連線的第一個 PUBLISH 封包,並與主題別名建立關聯。
  • 將主題名稱和主題別名屬性空白的其餘封包放入相同主題。

遷移

本節會列出與先前的 MQTT 支援相比,在此 API 中所做的變更。

  • 傳輸通訊協定現在是 MQTT 5。 先前:MQTT 3.1.1。
  • SAS 驗證的內容資訊會直接納入 CONNECT 封包中,而非與簽章一同編碼。
  • 「驗證方法」可用於指出使用的驗證方法。
  • 「共用存取簽章」現在放在驗證資料屬性中, 先前是使用 [密碼] 欄位。
  • 作業的主題不同:
    • 遙測:$iothub/telemetry 而非 devices/{Client Id}/messages/events
    • 命令:$iothub/commands 而非 devices/{Client Id}/messages/devicebound
    • 回報的修補檔對應項:$iothub/twin/patch/reported 而非 $iothub/twin/PATCH/properties/reported
    • 通知對應項預期狀態已變更:$iothub/twin/patch/desired 而非 $iothub/twin/PATCH/properties/desired
  • 不需要訂閱用戶端-伺服器要求-回應作業的回應主題。
  • 會使用使用者屬性,而不是在主題名稱區段中編碼屬性。
  • 屬性名稱採用「以破折號連接」的拼字命名慣例,而不是使用特殊前置詞的縮寫。 使用者定義的屬性現在需要使用前置詞, 例如 $.mid 現在是 message-id,而 myProperty1 會變成 @myProperty1
  • 相互關聯資料屬性現在用於為要求-回應作業的要求和回應訊息建立相互關聯,而非在主題中編碼的 $rid 屬性。
  • iothub-connection-auth-method 屬性不再標記在遙測事件上。
  • 裝置若沒有訂閱,就不會清除 C2D 命令, 命令會保留在佇列中,直到裝置進行訂閱或到期為止。

範例

傳送遙測

訊息:

-> PUBLISH
    QoS: 1
    Packet_Id: 31
    Topic: $iothub/telemetry
    @myProperty1: My String Value # optional
    creation-time: 1600987195320 # optional
    @ No_Rules-ForUser-PROPERTIES: Any UTF-8 string value # optional
    Payload: <data>

確認:

<- PUBACK
    Packet_Id: 31
    Reason_Code: 0

替代通知 (節流):

<- PUBACK
    Packet_Id: 31
    Reason_Code: 151
    status: 0501

傳送取得對應項狀態

要求:

-> PUBLISH
    QoS: 0
    Topic: $iothub/twin/get
    Correlation_Data: 0x01 0xFA
    Payload: <empty>

回應 (成功):

<- PUBLISH
    QoS: 0
    Topic: $iothub/responses
    Correlation_Data: 0x01 0xFA
    Payload: <twin/desired state>

回應 (不允許):

<- PUBLISH
    QoS: 0
    Topic: $iothub/responses
    Correlation_Data: 0x01 0xFA
    status: 0102
    reason: Operation not allowed for `B2` SKU
    Payload: <empty>

處理直接方法呼叫

要求:

<- PUBLISH
    QoS: 0
    Topic: $iothub/methods/abc
    Correlation_Data: 0x0A 0x10
    Payload: <data>

回應 (成功):

-> PUBLISH
    QoS: 0
    Topic: $iothub/responses
    Correlation_Data: 0x0A 0x10
    response-code: 200 # user defined response code
    Payload: <data>

注意

status 未設定:這是成功的回應。

裝置無法使用回應:

-> PUBLISH
    QoS: 0
    Topic: $iothub/responses
    Correlation_Data: 0x0A 0x10
    status: 0603

使用 QoS 0 時發生錯誤,第 1 部分

要求:

-> PUBLISH
    QoS: 0
    Topic: $iothub/twin/gett # misspelled topic name - server won't recognize it as Request-Response interaction
    Correlation_Data: 0x0A 0x10
    Payload: <data>

回應:

<- DISCONNECT
    Reason_Code: 144
    reason: "Unsupported topic: `$iothub/twin/gett`"

使用 QoS 0 時發生錯誤,第 2 部分

要求:

-> PUBLISH # missing Correlation Data
    QoS: 0
    Topic: $iothub/twin/get
    Payload: <data>

回應:

<- DISCONNECT
    Reason_Code: 131
    status: 0100
    reason: "`Correlation Data` property is missing"

下一步