如何使用 MQTT 報文實現發布訂閱功能


MQTT 協議通過交換預定義的MQTT控制報文來通信。下面以 MQTTX 為例,展示如何通過 MQTT 報文實現發布訂閱功能。

Connect 連接

MQTT 協議基於 TCP/IP 協議,MQTT Broker 和 Client 都有需要有 TCP/IP 地址。

Broker

WX201911281137582x.png

如果你暫時沒有一個可用的 MQTT Broker,EMQ X 提供了一個公共 Broker 地址用於測試:broker.emqx.io:1883

Client

WX201911281139012x.png

MQTTX 工具中 Client 的配置其實是 MQTT 協議中 Connect 報文的配置,下面解釋一下相關配置項:

Client ID

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

ClientId 必須存在,但是服務端可以允許客戶端提供一個零字節的 ClientId,如果這樣做了,服務端必須將這看作特殊情況並分配唯一的 ClientId 給那個客戶端。然后正常處理這個 CONNECT 報文。

Username/Password

MQTT 可以通過發送用戶名和密碼來進行相關的認證和授權,但是,如果此信息未加密,則用戶名和密碼是以明文的方式發送的。EMQ X 不僅支持SSL/TLS 加密,還提供了 emqx-auth-username 插件對密碼進行加密。

Keep Alive

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

如果 Keep Alive 的值非零,並且服務端在一點五倍的 Keep Alive 時間內沒有收到客戶端的控制報文,它必須斷開客戶端的網絡連接,認為網絡連接已斷開。

Clean Session

客戶端和服務端可以保存會話狀態,以支持跨網絡連接的可靠消息傳輸,這個標志告訴服務器這次連接是不是一個全新的連接。

客戶端的會話狀態包括:

  • 已經發送給服務端,但是還沒有完成確認的 QoS 1 和 QoS 2 級別的消息
  • 已從服務端接收,但是還沒有完成確認的 QoS 2 級別的消息。

服務端的會話狀態包括:

  • 會話是否存在,即使會話狀態的其它部分都是空。
  • 客戶端的訂閱信息。
  • 已經發送給客戶端,但是還沒有完成確認的 QoS 1 和 QoS 2 級別的消息。
  • 即將傳輸給客戶端的 QoS 1和 QoS 2 級別的消息。
  • 已從客戶端接收,但是還沒有完成確認的 QoS 2 級別的消息。
  • 可選,准備發送給客戶端的 QoS 0 級別的消息。

如果 CleanSession 標志被設置為 1,客戶端和服務端必須丟棄之前的任何會話並開始一個新的會話。會話僅持續和網絡連接同樣長的時間。

如果 CleanSession 標志被設置為 0,服務端必須基於當前會話(使用 ClientId 識別)的狀態恢復與客戶端的通信。如果沒有與這個客戶端標識符關聯的會話,服務端必須創建一個新的會話。當連接斷開后,客戶端和服務端必須保存會話信息。

Connack 確認連接請求

客戶端發送 Connect 報文請求對服務器的連接,服務器必須發送 Connack 報文作為對 來自客戶端的 Connect 報文的回應。如果客戶端在合理的時間內沒有收到服務端的CONNACK報文,客戶端應該關閉網絡連接。合理的時間取決於應用的類型和通信基礎設施。在 MQTTX 中,可以通過 Connection Timeout 來設置合理的超時時間。

Connect.png

Connack 報文包含 Session Present 和 Connect Return code 兩個重要的標志。

Session Present

Session Present 標志表示當前會話是否是一個新的會話,如果服務端收到 CleanSession 標志為1的連接,Connack報文中的 SessionPresent 標志為 0 。如果服務端收到一個 CleanSession 為0的連接,SessionPresent 標志的值取決於服務端是否已經保存了 ClientId 對應客戶端的會話狀態。如果服務端已經保存了會話狀態,Connack 報文中的 SessionPresent 標志為 1,如果服務端沒有已保存的會話狀態,Connack 報文中的 SessionPresent 標志為 0.

Connect Return code

Connect Return code 表示服務器對此次 Connect 的回應,0 表示連接已被服務器接受。如果服務端收到一個合法的 CONNECT 報文,但出於某些原因無法處理它,服務端應該嘗試發送一個包含非零返回碼(表格中的某一個)的 CONNACK 報文。如果服務端發送了一個包含非零返回碼的CONNACK 報文,那么它必須關閉網絡連接。

返回碼響應 描述
0 0x00連接已接受 連接已被服務端接受
1 0x01連接已拒絕,不支持的協議版本 服務端不支持客戶端請求的MQTT協議級別
2 0x02連接已拒絕,不合格的客戶端標識符 客戶端標識符是正確的UTF-8編碼,但服務端不允許使用
3 0x03連接已拒絕,服務端不可用 網絡連接已建立,但MQTT服務不可用
4 0x04連接已拒絕,無效的用戶名或密碼 用戶名或密碼的數據格式無效
5 0x05連接已拒絕,未授權 客戶端未被授權連接到此服務器
6-255 保留

如果認為上表中的所有連接返回碼都不太合適,那么服務端必須關閉網絡連接,不需要發送CONNACK 報文。

Subscribe 訂閱主題

客戶端向服務端發送 Subscribe 報文用於創建一個或多個訂閱。每個訂閱注冊客戶端關心的一個或多個主題。為了將應用消息轉發給與那些訂閱匹配的主題,服務端發送 Publish 報文給客戶端。Subscribe 報文為每個訂閱指定了最大的 QoS 等級,服務端根據這個發送應用消息給客戶端。

WX201911281425432x.png

Subscribe 報文的有效載荷必須包含至少一對主題過濾器 和 QoS 等級字段組合。沒有有效載荷的 Subscribe 報文是違反協議的。

使用 MQTTX 連接 broker.emqx.io:1883 的 Broker 並創建主題為testtopic/# ,Qos 等於 2 的訂閱。

WX201911281439252x.png

Suback 訂閱確認

服務端發送 Suback 報文給客戶端,用於確認它已收到並且正在處理 Subscribe 報文。

Subscribe.png

Suback 報文包含一個原因碼列表,用於指定授予的最大QoS等級或 Subscribe 報文所請求的每個訂閱發生的錯誤,每個原因碼對應 Subscribe 報文中的一個主題過濾器。Suback 報文中的原因碼順序必須與 Subscribe 報文中的主題過濾器順序相匹配

允許的返回碼值:

  • 0x00 - 最大QoS 0
  • 0x01 - 成功 – 最大QoS 1
  • 0x02 - 成功 – 最大 QoS 2
  • 0x80 - Failure 失敗

Publish 發布消息

Publish 報文是指從客戶端向服務端或者服務端向客戶端傳輸一個應用消息,服務器收到 Publish 報文后根據主題過濾器將消息轉發給其他客戶端。

Publish.png

嘗試使用 MQTTX 發布一條主題為 testtopic/mytopic ,內容為 {"msg": "hello world"} 的消息,由於之前已經訂閱了 testtopic/# 這一主題,所以立即接收到了 Broker 轉發回來的這條消息

WX201911281441422x.png

Topic

主題名(Topic Name)用於識別消息應該被發布到哪一個會話,服務端發送給訂閱客戶端的 Publish 報文的主題名必須匹配該訂閱的主題過濾器。

QoS

QoS 表示應用消息分發的服務質量等級保證

QoS值 Bit 2 Bit 1 描述
0 0 0 最多分發一次
1 0 1 至少分發一次
2 1 0 只分發一次
- 1 1 保留位

Publish 報文不能將 QoS 所有的位設置為 1。如果服務端或客戶端收到 QoS 所有位都為 1 的 Publish 報文,它必須關閉網絡連接。

關於不同等級的QoS的工作原理,請查閱MQTT 5.0 協議介紹 - QoS 服務質量

Retain

如果客戶端發給服務端的 Publish 報文的保留(RETAIN)標志被設置為 1,服務端必須存儲這個應用消息和它的服務質量等級(QoS),以便它可以被分發給未來的主題名匹配的訂閱者 。一個新的訂閱建立時,對每個匹配的主題名,如果存在最近保留的消息,它必須被發送給這個訂閱者。如果服務端收到一條保留(RETAIN)標志為1的Q消息,它必須丟棄之前為那個主題保留的任何消息,並將這個新的消息當作那個主題的新保留消息。

保留標志為 1 且有效載荷為零字節的 Publish 報文會被服務端當作正常消息處理,它會被發送給訂閱主題匹配的客戶端。此外,同一個主題下任何現存的保留消息必須被移除,因此這個主題之后的任何訂閱者都不會收到一個保留消息

服務端發送 Publish 報文給客戶端時,如果消息是作為客戶端一個新訂閱的結果發送,它必須將報文的保留標志設為 1。當一個 Publish 報文發送給客戶端是因為匹配一個已建立的訂閱時,服務端必須將保留標志設為 0,不管它收到的這個消息中保留標志的值是多少。

如果客戶端發給服務端的 Publish 報文的保留標志位 0,服務端不能存儲這個消息也不能移除或替換任何現存的保留消息。

Payload

有效載荷包含將被發布的應用消息。數據的內容和格式是應用特定的,可以發送圖像,任何編碼的文本,加密的數據以及幾乎所有二進制數據。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM