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 支援目前處於預覽狀態,且有下列限制 (除非另有明確註明,否則會透過 CONNACK
屬性與用戶端通訊):
- 尚不支援官方 Azure IoT 裝置 SDK。
- 不支援訂閱識別碼。
- 不支援共用訂閱。
- 不支援
RETAIN
。 Maximum QoS
為1
。Maximum Packet Size
為256 KiB
(各項作業可能有其他限制)。- 不支援指派的用戶端識別碼。
Keep Alive
限制為19 min
(活躍度檢查的最大延遲:28.5 min
)。Topic Alias Maximum
為10
。- 不支援
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
:共用存取簽章透過CONNECT
的Authentication 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 Id
是CONNECT
封包中的用戶端識別碼。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 中定義的任何自訂錯誤。status
和reason
屬性會用於傳達與連線中斷原因相關的詳細資料 (詳情請參閱回應)。
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
回應 (詳情請參閱回應), - 訂閱未定義的主題會得到
SUBACK
與Reason Code: 0x8F
(主題篩選不正確)。
主題名稱和屬性名稱會區分大小寫,且必須完全相符。 例如,系統支援 $iothub/telemetry
,但不支援 $iothub/telemetry/
。
注意
不支援在訂閱中於 $iothub/..
後方使用萬用字元。 換句話說,用戶端無法訂閱 $iothub/+
或 $iothub/#
, 若嘗試這樣做會傳回 SUBACK
與 Reason Code: 0xA2
(不支援萬用字元訂閱)。 僅支援單一區段萬用字元 (+
),而非有萬用字元之作業主題名稱中的路徑參數。
互動類型
此 API 中所有作業都是以下列兩種互動類型的其中一個為依據:
- 具有選擇性通知的訊息 (MessageAck)
- 要求-回應 (ReqRep)
作業也會因方向 (取決於交換中的初始訊息方向) 而有所不同:
- 用戶端至伺服器 (c2s)
- 伺服器至用戶端 (s2c)
例如,傳送遙測是「具有通知的訊息」類型的用戶端至伺服器作業,而處理直接方法是「要求-回應」類型的伺服器至用戶端作業。
訊息通知互動
具有選擇性通知的訊息 (MessageAck) 互動會以交換 MQTT 中的 PUBLISH
與 PUBACK
封包表示。 通知是選擇性的,而且傳送者可以選擇不透過 傳送 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: 1
的 PUBLISH
封包當作要求或回應訊息, 若傳送 PUBLISH
要求會得到 Bad Request
回應。
屬性 Correlation Data
中支援的長度上限為 16 個位元組。 如果將 PUBLISH
封包上的 Correlation Data
屬性設為超過 16 個位元組的值,則 IoT 中樞會傳送 DISCONNECT
與 Bad Request
結果,並關閉連線。 此行為僅適用於在這個 API 內交換的封包。
注意
相互關聯資料是任意位元組序列,例如不能保證為 UTF-8 字串。
ReqRep 使用預先定義的主題進行回應;要求 PUBLISH
封包中的回應主題屬性 (若由傳送者設定) 會受到忽略。
IoT 中樞會自動為所有用戶端至伺服器 ReqRep 作業訂閱回應主題用戶端。 即使用戶端明確取消訂閱回應主題,IoT 中樞依然會自動恢復訂閱。 針對伺服器至用戶端的 ReqRep 互動,裝置仍需訂閱。
郵件內容
(由系統或使用者定義的) 作業屬性在 MQTT 5 中以封包屬性表示。
使用者屬性名稱會區分大小寫,且必須完全符合定義的拼字, 例如,系統支援 trace-id
,但不支援 Trace-ID
。
要求若具有不包含在規格中的使用者屬性,且沒有前置詞 @
,即會發生錯誤。
系統屬性會編碼為第一級屬性 (例如 Content Type
) 或使用者屬性。 規格提供完整的支援系統屬性清單,
除非規格中明確表示提供支援,否則所有第一級屬性都會受到忽略。
使用者定義屬性如獲准使用,其名稱必須遵循 @{property name}
格式。 使用者定義的屬性只支援有效的 UTF-8 字串值, 例如,值為 15
的 MyProperty1
屬性必須編碼為名稱是 @MyProperty
且值是 15
的使用者屬性。
如果 IoT 中樞無法辨識使用者屬性,則視為發生錯誤,IoT 中樞會以 PUBACK
與 Reason 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 位元整數值
回應
互動可能得到 Success
、Bad Request
、Not Found
等多種結果,
結果會依 status
使用者屬性進行區分。 PUBACK
封包中的 Reason Code
(MessageAck 互動) 在意義上會盡可能符合 status
。
注意
若用戶端在 CONNECT 封包中指定 Request Problem Information: 0
,那麼系統不會為了遵循 MQTT 5 規格而透過 PUBACK
封包傳送任何使用者屬性 (包含 status
屬性)。 在這種情況下,用戶端仍可仰賴 Reason Code
判斷通知為正值還是負值。
每個互動都有預設值 (或成功), Reason Code
為 0
且 status
屬性為「未設定」, 否則:
- 針對 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
第二個位元組包含實際的不同回應碼。 具有不同旗標的錯誤碼可以有相同的第二個位元組值, 例如 0001
、0101
、0201
、0301
錯誤碼的意義都不同。
例如,Too Many Requests
是用戶端,自帶代碼 1
的可重試錯誤, 其值為 0000 0101 0000 0001
或 0x0501
。
用戶端可使用類型位元來識別作業是否成功結束, 也能使用可重試位元決定重試作業是否合理。
建議
工作階段管理
CONNACK
封包具有 Session Present
屬性,能指出伺服器是否已還原先前建立的會話。 請使用這個屬性了解要訂閱主題還是略過稍早已完成的訂閱。
如要仰賴 Session Present
,用戶端必須追蹤其訂閱 (也就是傳送 SUBSCRIBE
封包,並接收 SUBACK
與成功的原因代碼),或確認在單一 SUBSCRIBE
/SUBACK
交換中訂閱所有主題, 否則用戶端若傳送兩個 SUBSCRIBE
封包,而伺服器只成功處理其中一個封包,那麼伺服器會在 CONNACK
中傳達 Session Present: 1
,且只接受部分用戶端訂閱。
為了避免發生舊版用戶端未訂閱所有主題的情況,建議在用戶端行為變更時無條件訂閱 (例如在韌體更新時執行)。 此外,為了確保不會留下過時的訂閱 (從允許訂閱數上限取得),請明確取消不再使用的訂閱。
批次處理
傳送訊息批次不需使用特殊格式。 若要減少 TLS 和網路中會耗用大量資源的作業所帶來的額外負荷,請先將封包 (PUBLISH
、PUBACK
、SUBSCRIBE
等) 組合在一起,再提供給基礎 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"
下一步
- 如要檢閱 MQTT 5 預覽 API 參考,請參閱 IoT 中樞資料平面 MQTT 5 API 參考 (預覽)。
- 若要遵循 C# 範例,請參閱 GitHub 範例存放庫 (英文)。