MQTT 協議是當今世界上最受歡迎的物聯網協議,沒有之一。MQTT 協議為裝置提供了穩定、可靠、簡單易用的通訊基礎,截至目前透過 MQTT 協議連線的裝置已經過億,廣泛應用於 IoT、M2M 等領域。本篇將從最基礎的知識開始,向您講解 MQTT 協議的原理與應用。
目前 MQTT 主流版本有 MQTT3.1.1 和 MQTT5。MQTT5 完全相容 MQTT3.1.1,是在 MQTT3.1.1 的基礎上進行完善補充。目前 MQTT3.1.1 的使用人數還是更多,所以本文用 MQTT3.1.1 來講解。
0. 原始碼下載及前置閱讀
本文首發 良許嵌入式網 :https://www.lxlinux.net/e/ ,歡迎關注!
本文所涉及的原始碼及安裝包如下(由於平臺限制,請點選以下連結閱讀原文下載):
https://www.lxlinux.net/e/stm32/mqtt-turorial.html
如果你是嵌入式開發小白,那麼建議你先讀讀下面幾篇文章。
- STM32下載程式的五種方法:https://www.lxlinux.net/e/stm32/five-ways-to-flash-program-to...
- 一文教你使用MDK開發工具:https://www.lxlinux.net/e/stm32/mdk-development-tool-tutorial...
- 零基礎快速上手STM32開發(手把手保姆級教程):https://www.lxlinux.net/e/stm32/stm32-quick-start-for-beginne...
1. MQTT是什麼
MQTT(Message Queuing Telemetry Transport,訊息佇列遙測傳輸)協議是基於 TCP/IP 協議棧構建的非同步通訊訊息協議,是一種輕量級的、客戶端服務端架構的、釋出/訂閱模式的訊息傳輸協議。
MQTT協議最初版本是在1999年建立的,發明人是 Andy Stanford-Clark 和 Arlen Nipper。MQTT 是他們為了利用衛星通訊監控輸油管道所開發的協議,由此可見,MQTT 就是專為低頻寬、高延遲或不可靠的網路而設計的。
MQTT 協議特點:
- 簡單易用,方便整合
- 安全可靠,支援TLS/SSL加密和認證機制
- 輕量級,佔用頻寬小,支援多種訊息傳輸模式
- 靈活性,可知裝置連線狀態,可控資料傳輸質量
2. MQTT原理
在 MQTT 協議通訊中,最重要的兩個角色是服務端和客戶端。客戶端向一「主題」「釋出」訊息,服務端處理並推送給「訂閱」了該「主題」的其他客戶端。
這麼說是不是一頭霧水?我打個比方,將整個 MQTT 比作我們熟悉的影片軟體,一一對應關係如下。
抖音 | MQTT |
---|---|
抖音伺服器 | 服務端 |
所有抖音使用者 | 客戶端 |
抖音使用者關注某個抖音博主 | 訂閱 |
博主釋出影片 | 釋出 |
假如你是張三,一名普通的抖音使用者,你關注了良許的抖音賬號。在這裡,張三跟良許不會直接產生關係,而是會透過抖音伺服器。抖音伺服器就是「服務端」,所有抖音使用者就是「客戶端」,你關注良許的這個動作,就叫作「訂閱」。
良許如果「釋出」了一條影片,那麼張三、李四、王五、老六,等等所有關注了良許的粉絲都會收到這個影片推送。這是因為抖音裡沒有主題的概念,只要良許有發影片,粉絲都會收到推送。
假如抖音也有主題的概念,釋出的影片都帶有主題的屬性。那麼,良許釋出了程式設計、副業、職場、吃喝拉撒相關主題的影片,而張三隻訂閱了吃喝拉撒這個「主題」,那麼只有當良許釋出了吃喝拉撒這個主題的影片,張三才會收到這個影片。而如果釋出了程式設計、副業相關的影片,張三不會收到任何通知。
這就是 MQTT 的基礎概念。
2.1 服務端
MQTT 服務端通常是一臺伺服器,它充當著 MQTT 資訊傳輸的中心節點。其主要功能是接收來自 MQTT 客戶端的資訊並將其傳遞給其他 MQTT 客戶端。此外,MQTT 服務端還負責管理客戶端,確保客戶端之間的通訊暢通無阻,並確保 MQTT 訊息被正確接收和準確投遞。
服務端一般就是雲平臺,OneNET、阿里雲、騰訊雲等;也可以用 EMQ 或 Mosquitto 自己搭建服務端。
2.2 客戶端
MQTT 客戶端可以向服務端透過「釋出」傳送資訊,也可以從服務端「訂閱」來收取資訊。
客戶端一般就是我們的微控制器,STM32、C51、樹莓派等。
2.3 主題
在 MQTT 通訊中,客戶端訂閱的是一個個「主題」。MQTT 服務端在管理資訊通訊時,使用「主題」來控制。
2.4 釋出與訂閱的特點
- 相互獨立:客戶端相互獨立,彼此沒有直接聯絡,不用知道對方的任何狀態、情況。
- 空間分離:客戶端只要連線同一個 MQTT 通訊網路,無論是網際網路或者區域網都可以通訊。
- 時間非同步:客戶端的釋出與訂閱無需同步。若有客戶端斷連,服務端儲存資訊,待客戶端上線後推送。
3. MQTT報文
MQTT 協議透過交換預定義的 MQTT 控制報文來通訊。MQTT 控制報文簡稱 MQTT 報文,接下來我將詳細介紹 MQTT 報文。
3.1 報文結構
一個 MQTT 報文由固定報頭、可變報頭、有效載荷三部分組成:
- 固定報頭(Fixed header),所有 MQTT 報文有,表示報文型別及報文的分組類標識。
- 可變報頭(Variable header),部分 MQTT 報文有,報文型別決定了可變頭是否存在及其具體內容。
- 有效載荷/訊息體(Payload),部分 MQTT 報文有,存放報文的具體內容。
示意圖如下:
整體的報文結構介紹完,下面介紹每個的細節。如果看不懂,沒關係,後面會有案例,更加清晰明瞭。
3.2 固定報頭(Fixed header)
3.2.1 訊息型別(message type)
位於 byte 1 的第 7~4 位,表示 MQTT 報文型別,有下面這麼多型別:
名稱 | 值 | 報文流動方向 | 描述 |
---|---|---|---|
Reserved | 0 | 禁止 | 保留位 |
CONNECT | 1 | 客戶端到伺服器 | 客戶端請求連線到伺服器 |
CONNACK | 2 | 伺服器到客戶端 | 連線確認 |
PUBLISH | 3 | 雙向 | 釋出訊息 |
PUBACK | 4 | 雙向 | 釋出確認 |
PUBREC | 5 | 雙向 | 釋出收到(保證第1部分到達) |
PUBREL | 6 | 雙向 | 釋出釋放(保證第2部分到達) |
PUBCOMP | 7 | 雙向 | 釋出完成(保證第3部分到達) |
SUBSCRIBE | 8 | 客戶端到伺服器 | 客戶端請求訂閱 |
SUBACK | 9 | 伺服器到客戶端 | 訂閱確認 |
UNSUBSCRIBE | 10 | 客戶端到伺服器 | 請求取消訂閱 |
UNSUBACK | 11 | 伺服器到客戶端 | 取消訂閱確認 |
PINGREQ | 12 | 客戶端到伺服器 | 心跳請求 |
PINGRESP | 13 | 伺服器到客戶端 | 心跳響應 |
DISCONNECT | 14 | 客戶端到伺服器 | 中斷連線 |
Reserved | 15 | 禁止 | 保留位 |
3.2.2 標誌位(DUP、QoS Level、RET)
位於 byte 1 的第 3~0 位,表示 MQTT 報文的分組類標識。在不使用標識位的訊息型別中,標識位被做為保留位。如果收到無效的標誌時,接收端必須關閉網路連線。
- DUP:釋出訊息的副本
- QoS:釋出訊息的服務質量
- RETAIN:釋出保留標識
控制報文 | 固定報頭標誌 | Bit 3 | Bit 2 | Bit 1 | Bit 0 |
---|---|---|---|---|---|
CONNECT | 保留位 | 0 | 0 | 0 | 0 |
CONNACK | 保留位 | 0 | 0 | 0 | 0 |
PUBLISH | MQTT 3.1.1使用 | DUP1 | QoS2 | QoS2 | RETAIN3 |
PUBACK | 保留位 | 0 | 0 | 0 | 0 |
PUBREC | 保留位 | 0 | 0 | 0 | 0 |
PUBREL | 保留位 | 0 | 0 | 1 | 0 |
PUBCOMP | 保留位 | 0 | 0 | 0 | 0 |
SUBSCRIBE | 保留位 | 0 | 0 | 1 | 0 |
SUBACK | 保留位 | 0 | 0 | 0 | 0 |
UNSUBSCRIBE | 保留位 | 0 | 0 | 1 | 0 |
UNSUBACK | 保留位 | 0 | 0 | 0 | 0 |
PINGREQ | 保留位 | 0 | 0 | 0 | 0 |
PINGRESP | 保留位 | 0 | 0 | 0 | 0 |
DISCONNECT | 保留位 | 0 | 0 | 0 | 0 |
3.2.3 剩餘長度(Remaining Length)
位於 byte 2 的第 3~0 位,表示當前剩餘位元組數,包括可變報頭和負載的資料。剩餘長度不包括用於編碼剩餘長度欄位本身的位元組數。
3.3 可變報頭(Variable header)
某些 MQTT 報文有可變頭。它在固定頭和有效載荷之間。可變頭的內容根據報文型別的不同而不同。可變頭的報文識別符號欄位存在於在多個型別的報文裡。
可變報頭在後續的報文案例中會詳細介紹。
3.4 有效載荷(Payload)
有效載荷就是應用訊息,但並不是所有的報文都有有效載荷,只有部分 MQTT 報文有有效載荷,具體如下:
控制報文 | 有效載荷 |
---|---|
CONNECT | 需要 |
CONNACK | 不需要 |
PUBLISH | 可選 |
PUBACK | 不需要 |
PUBREC | 不需要 |
PUBREL | 不需要 |
PUBCOMP | 不需要 |
SUBSCRIBE | 需要 |
SUBACK | 需要 |
UNSUBSCRIBE | 需要 |
UNSUBACK | 不需要 |
PINGREQ | 不需要 |
PINGRESP | 不需要 |
DISCONNECT | 不需要 |
4. QoS,服務質量
QoS(Quality of Service,服務質量)。在資料通訊的過程中,有的訊息很重要,不可以丟失;有的訊息不重要,丟了也沒關係。所以在 MQTT 中可以配置 QoS,給不同重要的訊息不同的服務質量。
MQTT 協議有三種服務質量級別:
- QoS = 0:最多發一次
- QoS = 1:最少發一次
- QoS = 2:保證收一次
對於不同重要級的訊息選擇不同的 QoS,較為重要訊息的使用 QoS = 1 和 QoS = 2。
4.1 QoS = 0:最多發一次
這種服務質量訊息最多隻傳送一次。接收者不會傳送響應,傳送者也不會重試。訊息可能送達一次也可能根本沒送達。
想象你是一個快遞員,而你要將包裹(訊息)送到不同的收件人(訂閱者)。QoS 級別就像你和收件人之間的交付服務等級,它決定了你在送貨過程中提供的保證。QoS 0(最多發一次)相當於你將包裹送給收件人後,沒有任何確認回執。你只是簡單地把包裹放在門口,然後離開。在這種情況下,你無法確定包裹是否成功被收件人接收,也無法知道是否有其他人偷了這個包裹。
4.2 QoS = 1:最少發一次
服務質量確保訊息至少送達一次。QoS 1 的 PUBLISH 報文的可變報頭中包含一個報文識別符號,需要 PUBACK 報文確認。
QoS 1(最少發一次)相當於你在送貨後要求收件人給你一個回執確認。你將包裹送給收件人,然後等待他給你一個回執,告訴你已經收到包裹。如果你沒有收到回執,你會重新嘗試送貨,直到收到回執為止。這樣,你可以確保包裹被收件人接收,但可能會增加一些延遲和工作量。
4.3 QoS = 2:保證收一次
這是最高等級的服務質量,訊息丟失和重複都是不可接受的。使用這個服務質量等級會有額外的開銷。QoS 2 的訊息可變報頭中有報文識別符號。QoS 2 的 PUBLISH 報文的接收者使用一個兩步確認過程來確認收到。
QoS 2(保證收一次)相對於你要確認對方可以收貨再發貨。你在送貨前給收件人發訊息問他在不在家,收件人告訴你他在家,你把將包裹送給收件人,然後等待他給你一個回執,告訴你已經收到包裹。如果他沒回訊息,不在家,就繼續發訊息直到收件人回訊息,告訴你他在家,再送包裹。
5. MQTT心跳機制
MQTT心跳機制可以比喻為人體的心臟跳動,兩者都是為了維持正常的執行狀態和連線的穩定性。
當MQTT客戶端定期傳送心跳包時,它就像是我們的心臟,定時地向伺服器傳送訊號,表明自己的存在和健康狀況。如果伺服器在一定時間內沒有接收到心跳包,就會認為客戶端出現異常或離線,類似於身體出現問題時,醫生會檢查心跳情況來判斷身體的健康狀況。
客戶端定時向服務端傳送心跳請求(PINGREQ),告訴服務端,我還和你連線著哦。服務端收到心跳請求後,會回覆一條心跳響應(PINGRESP),告訴客戶端,我知道你還連著我啦。
透過心跳機制,MQTT 可以實時監測客戶端的連線狀態,及時發現和處理異常情況,確保通訊的可靠性和穩定性。就像我們依賴心臟維持身體的正常運轉一樣,MQTT的心跳機制也是保障通訊鏈路順暢執行的重要機制之一。
6. MQTT遺囑
遺囑,和前面的心跳一樣,有心跳請求證明客戶端還連著服務端,客服端還活著。那麼遺囑就很生動形象了,客戶端先把自己的遺囑給服務端,萬一客戶端嘎了,服務端就可以執行遺囑了。
MQTT遺囑是一種機制,允許客戶端在「活著」的時候設定併傳送遺囑訊息,以便在客戶端意外斷線時由服務端公佈。
意外斷線指的是當客戶端在沒有傳送 DISCONNECT 報文的情況下失去了心跳訊號,這通常發生在網路故障或電池耗盡等情況下。此時,服務端會察覺到客戶端的異常斷開,並將客戶端的遺囑訊息釋出出來。然而,如果客戶端正常斷開連線併傳送了 DISCONNECT 報文,遺囑則不會啟動,服務端也不會發布客戶端的遺囑訊息。
透過合理設定和使用 MQTT 遺囑機制,可以增強客戶端在服務端管理中的作用,並提供實時的裝置狀態資訊。
7. 報文案例
前面的 MQTT 報文是不是有很多同學聽的雲裡霧裡的,現在我們就上案例,直觀明瞭,包教包會!
報文講解超詳細,文章會比較長哦,大家一定要耐住性子。
我們需要建立產品和裝置,先給大家介紹一下產品和裝置的區別:
- 產品:一組具有相同功能定義的裝置集合,產品下的資源包括裝置、裝置資料、裝置許可權、資料觸發服務以及基於裝置資料的應用等多種資源,使用者可以建立多個產品。
- 裝置:歸屬於某一個產品下,是真實裝置在平臺的對映,用於和真實裝置透過連線報文建立連線關係,平臺資源分配的最小單位,裝置之間的透過裝置名稱來區分。
通俗易懂的來說,產品就好比是蘋果手機,裝置就是蘋果11、蘋果14、蘋果15、蘋果15 pro、蘋果15 pro max 等等。
7.1 OneNET配置
在詳細介紹報文案例前需要配置一下 OneNET。動動小手跟我操作起來。
點開 OneNET 官方網址:中移坤靈 - 中國移動物聯網開放平臺 (10086.cn)
有賬號的登入,沒賬號的註冊一下。
登入好後點選「開發者中心」。
接下來我們先建立一個產品,之後再建立具體的裝置。
關於產品與裝置的區別,可以翻看上面的介紹。
接下來就開始建立產品下的具體裝置。
儲存好「MQTT 三元組」:
- 裝置 ID:test1
- 產品 ID:L14FCC38pq
- 裝置金鑰:b0ZHZ1BnNTdBSUV2c0dhUmJGMDBRYVJXS090VEVnMHU=
7.3 CONNECT報文
接下來我會以 MQTT 報文的 CONNECT 報文為例,詳細的解釋 MQTT 報文的組成與意義。
PS:我們都知道,在計算機內部計算、通訊只有 0 和 1 ,但是二進位制對於程式設計師表達來說並不方便,常用的是十六進位制,因為一個兩位的十六進位制數剛好可以表達八位二進位制數也就是一位元的值,所以我們的例子會以十六進位制來表示二進位制報文。
如果有不會轉進位制和轉碼的朋友可以百度一下線上進位制轉換和線上編碼轉換,也可以像我一樣使用串列埠助手(後續會教)。
在此附上一張 ASCII 碼錶,裡面包含了常用的字元、十六進位制、二進位制轉換。
CONNECT 報文的作用是用於客戶端請求連線到伺服器。一條 CONNECT 報文就是以固定報頭、可變報頭、有效載荷三部分組成。示意如下:
固定報頭 | 可變報頭 | 有效荷載 | |
---|---|---|---|
字串 | liangxu | study | stm32 |
十六進位制 | 6C 69 61 6E 67 78 75 | 73 74 75 64 79 | 73 74 6D 33 32 |
然後把固定報頭、可變報頭、有效載荷三部分的十六進位制數拼起來就是 CONNET 報文了。
CONNECT 報文 = 固定報頭 + 可變報頭 + 有效載荷
= 6C 69 61 6E 67 78 75 + 73 74 75 64 79 + 73 74 6D 33 32
= 6C 69 61 6E 67 78 75 73 74 75 64 79 73 74 6D 33 32
是不是有點像火車?一列火車拉了三個車廂,每個車廂拉的是固定報頭、可變報頭、有效載荷的十六進位制數。
7.3.1 固定報頭
固定報頭由2個位元組組成,結構如下圖:
接著以 CONNECT 報文為例,結構如下圖,固定報頭中訊息型別是1,標誌位是 0000,所以第一位元組是 00010000,轉成十六進位制是 10,剩餘長度未知,所以 CONNECT 報文固定報頭的十六進位制是:10 XX。
7.3.2 可變報頭
可變報頭由協議名、協議級別、連線標誌、保持連線四個部分組成。基本是10個位元組。
以 CONNECT 報文為例:
協議名,由6個位元組組成,表示協議名 MQTT 的 UTF-8 編碼的字串,結構如下圖。
byte 1 和 byte 2 表示協議名後面部分的資料長度,這個長度是固定的 4 位。所以 byte 1 儲存 00 ,byte 4 儲存 04 。
而 byte 3~6 則固定為 M Q T T 這四個字元,每個位元組儲存成一個字元,對應就是 4D 51 54 54 。
所以協議名轉成十六進位制是 00 04 4D 51 54 54。
協議級別,只由1個位元組組成,對於協議 MQTT3.1.1 協議級別的值是 4,結構如下圖,轉成十六進位制是 04。
連線標誌,也只由1個位元組組成,包含一些用於指定 MQTT 連線行為的引數,還指出有效載荷中的欄位是否存在。結構如下圖,× 是表示不固定,可能是0,也可能是1,但是保留位(Reserved)必須是0。
從 0 到 7 位分別是保留位(Reserved)、清理會話(Clean Session)、遺囑標誌(Will Flag)、遺囑 QoS(Will QoS)、遺囑保留(Will Retain)、密碼標誌(Password Flag)、使用者名稱標誌(User Name Flag)。對應位填 1 則表示有,填 0 則無。
細心的同學就發現遺囑 QoS(Will QoS)佔了兩位,這是為什麼呢,因為服務質量等級有三級,我們二進位制表達需要兩位,如果是 00,則 QoS = 0;如果是 01,則 QoS = 1;如果是 10,則 QoS = 2。
這裡我們假設連線標誌有使用者名稱標誌(User Name Flag)、密碼標誌(Password Flag)和清理會話(Clean Session),那麼連線標誌的二進位制表示是 11000010,轉成十六進位制是 C2。
保持連線,由 2 個位元組組成,是一個以秒為單位的時間間隔,表示為一個 16 位的字,它是指在客戶端傳輸完成一個控制報文的時刻到傳送下一個報文的時刻,兩者之間允許空閒的最大時間間隔。
假設空閒的最大時間間隔是 100 秒,則結構如下圖,轉成十六進位制是 00 64。
我們剛剛已經以 CONNECT 報文的可變頭為例,將協議名、協議級別、連線標誌、保持連線都給出了例子,寫出了十六進位制。我們把這些十六進位制數按順序組合起來,CONNECT 報文的可變頭的十六進位制是:00 04 4D 51 54 54 04 C2 00 64。
7.3.3 有效載荷
有效載荷就是應用訊息,部分 MQTT 報文有有效載荷,有效載荷包含一個或多個以長度為字首的欄位,可變報頭中的標誌決定是否包含這些欄位(欄位要求以長度為字首是因為有效載荷的欄位要求以 UTF-8 編碼格式,其特點就是以一個兩位元組的長度作為字首)。
以 CONNECT 報文為例:有效載荷 = 裝置 ID + 產品 ID + token。裝置 ID、產品 ID、token 是在 MQTT 協議中用於服務端與客戶端對接的引數,缺一不可。
裝置 ID、產品 ID 是不是很熟悉?就是我們的「MQTT 三元組」,把我們剛剛儲存的引數拿出來。
- 裝置 ID:test1
- 產品 ID:L14FCC38pq
- 裝置金鑰:b0ZHZ1BnNTdBSUV2c0dhUmJGMDBRYVJXS090VEVnMHU=
可是,token 是什麼我們不知道,這裡先挖一個坑,token 在後面會介紹,它是由裝置密碼和一系列引數經過加密生成的。
那麼,透過前面的學習,我們知道 MQTT 報文需要將字串轉成二進位制(十六進位制表示),有效載荷中的欄位需要以長度為字首,那麼轉換過程如下,我們一步步來:
首先開啟串列埠助手,沒有的同學可以從本文開頭的連結拿。
以產品 ID:L14FCC38pq 為例。
長度字首由兩位十六進位制組成,資料長度 10 轉換成十六進位制是 0A,所以長度字首就是 00 0A。
PS:我們算長度字首要用十進位制轉十六進位制計算器,網上很多進位制轉換器都可以用,甚至手機的計算器就有進位制換算功能,當然,如果你數學很強,口算也是可以的。但是長度字首不能用串列埠助手轉換,如果長度字首也用串列埠助手的話就會出錯,如下圖,數字 10 會被認為是字元的 1 和 0 轉換成十六進位制。
裝置 ID 也和產品 ID 一樣操作,每次點選傳送前記得點復位計數哦,不然就重複計數啦,每步結果如下:
裝置 ID | 產品 ID | |
---|---|---|
字串 | test1 | L14FCC38pq |
十六進位制表示二進位制 | 74 65 73 74 31 | 4C 31 34 46 43 43 33 38 70 71 |
加上長度字首 | 00 05 74 65 73 74 31 | 00 0A 4C 31 34 46 43 43 33 38 70 71 |
裝置金鑰需要經過加密,加密需要用 OneNET 官方的 token 生成工具。
官網下載地址:OneNET - 中國移動物聯網開放平臺 (10086.cn)
也可以拿文章開頭提供的,也是官網下載的。
下載好 token 生成工具,開啟介面如下,我來告訴大家每個空填啥。
各個引數介紹如下表:
名稱 | 型別 | 引數說明 | 引數示例 |
---|---|---|---|
res | string | 訪問資源 resource 格式為:products/{產品id}/devices/{裝置名字} | products/L14FCC38pq/devices/test1 |
et | int | 訪問過期時間,單位秒,unix 時間。當一次訪問引數中的 et 時間小於當前時間時,平臺會認為訪問引數過期從而拒絕該訪問 | 2017881776 表示:北京時間 2033-12-11 10:42:56 |
key | string | MQTT 三元組的裝置金鑰 | b0ZHZ1BnNTdBSUV2c0dhUmJGMDBRYVJXS090VEVnMHU= |
method | string | 加密方式,支援 hmacmd5、hmacsha1、hmacsha256 | md5(代表使用hmacmd5演算法) sha1(代表使用hmacsha1演算法) sha256(代表使用hmacsha256 演算法) |
version | string | 引數組版本號,日期格式,目前僅支援"2018-10-31" | 2018-10-31 |
et 的時間戳可以用這個線上工具轉換,網頁地址:時間戳(Unix timestamp)轉換工具 - 線上工具 (tool.lu)
根據介紹,填好各個引數的空,我們選擇 sha1 的加密方式,大家可以選擇自己喜歡的。填好如下操作:
我們把得到的 token 按照前面利用串列埠助手的方式轉換,過程如下:
token | |
---|---|
字串 | version=2018-10-31&res=products%2FL14FCC38pq%2Fdevices%2Ftest1&et=2017881776&method=sha1&sign=8ukFaZ7tK%2B%2BpisREUYRLYcSiRVw%3D |
十六進位制表示二進位制 | 76 65 72 73 69 6F 6E 3D 32 30 31 38 2D 31 30 2D 33 31 26 72 65 73 3D 70 72 6F 64 75 63 74 73 25 32 46 4C 31 34 46 43 43 33 38 70 71 25 32 46 64 65 76 69 63 65 73 25 32 46 74 65 73 74 31 26 65 74 3D 32 30 31 37 38 38 31 37 37 36 26 6D 65 74 68 6F 64 3D 73 68 61 31 26 73 69 67 6E 3D 38 75 6B 46 61 5A 37 74 4B 25 32 42 25 32 42 70 69 73 52 45 55 59 52 4C 59 63 53 69 52 56 77 25 33 44 |
加上長度字首 | 00 80 76 65 72 73 69 6F 6E 3D 32 30 31 38 2D 31 30 2D 33 31 26 72 65 73 3D 70 72 6F 64 75 63 74 73 25 32 46 4C 31 34 46 43 43 33 38 70 71 25 32 46 64 65 76 69 63 65 73 25 32 46 74 65 73 74 31 26 65 74 3D 32 30 31 37 38 38 31 37 37 36 26 6D 65 74 68 6F 64 3D 73 68 61 31 26 73 69 67 6E 3D 38 75 6B 46 61 5A 37 74 4B 25 32 42 25 32 42 70 69 73 52 45 55 59 52 4C 59 63 53 69 52 56 77 25 33 44 |
接下來是不是很簡單啦,有效載荷 = 裝置 ID + 產品 ID + token = 00 05 74 65 73 74 31 00 0A 4C 31 34 46 43 43 33 38 70 71 00 80 76 65 72 73 69 6F 6E 3D 32 30 31 38 2D 31 30 2D 33 31 26 72 65 73 3D 70 72 6F 64 75 63 74 73 25 32 46 4C 31 34 46 43 43 33 38 70 71 25 32 46 64 65 76 69 63 65 73 25 32 46 74 65 73 74 31 26 65 74 3D 32 30 31 37 38 38 31 37 37 36 26 6D 65 74 68 6F 64 3D 73 68 61 31 26 73 69 67 6E 3D 38 75 6B 46 61 5A 37 74 4B 25 32 42 25 32 42 70 69 73 52 45 55 59 52 4C 59 63 53 69 52 56 77 25 33 44
注意:這裡的有效載荷僅適用 OneNET,每個平臺的 MQTT 報文基本一致,但是有效載荷會有一點不同。
7.3.4 組裝CONNECT報文
課堂提問時間:)
由上面的講解我們可知:
- 一條 CONNECT 報文是以固定報頭+可變報頭+有效載荷三部分組成。
- CONNECT 報文固定報頭的十六進位制是:10 XX。
- CONNECT 報文的可變頭的十六進位制是:00 04 4D 51 54 54 04 C2 00 64
- CONNECT 報文的有效載荷的十六進位制是:00 05 74 65 73 74 31 00 0A 4C 31 34 46 43 43 33 38 70 71 00 80 76 65 72 73 69 6F 6E 3D 32 30 31 38 2D 31 30 2D 33 31 26 72 65 73 3D 70 72 6F 64 75 63 74 73 25 32 46 4C 31 34 46 43 43 33 38 70 71 25 32 46 64 65 76 69 63 65 73 25 32 46 74 65 73 74 31 26 65 74 3D 32 30 31 37 38 38 31 37 37 36 26 6D 65 74 68 6F 64 3D 73 68 61 31 26 73 69 67 6E 3D 38 75 6B 46 61 5A 37 74 4B 25 32 42 25 32 42 70 69 73 52 45 55 59 52 4C 59 63 53 69 52 56 77 25 33 44
求 CONNECT 報文。
是不是很簡單,CONNECT 報文:10 XX 00 04 4D 51 54 54 04 C2 00 64 00 05 74 65 73 74 31 00 0A 4C 31 34 46 43 43 33 38 70 71 00 80 76 65 72 73 69 6F 6E 3D 32 30 31 38 2D 31 30 2D 33 31 26 72 65 73 3D 70 72 6F 64 75 63 74 73 25 32 46 4C 31 34 46 43 43 33 38 70 71 25 32 46 64 65 76 69 63 65 73 25 32 46 74 65 73 74 31 26 65 74 3D 32 30 31 37 38 38 31 37 37 36 26 6D 65 74 68 6F 64 3D 73 68 61 31 26 73 69 67 6E 3D 38 75 6B 46 61 5A 37 74 4B 25 32 42 25 32 42 70 69 73 52 45 55 59 52 4C 59 63 53 69 52 56 77 25 33 44。
這裡就有同學就要問了,那 XX 還是未知啊。是的,XX 是什麼還記得嗎?
XX 是剩餘長度,表示當前剩餘位元組數,包括可變報頭和負載的資料的長度。剩餘長度不包括用於編碼剩餘長度欄位本身的位元組數。簡單來說,XX 後面有多少個字元,XX 就是多少,剩餘長度就是多少。
拿出我們的串列埠助手,記得復位後再傳送哦。
於是乎,剩餘長度是159。當剩餘長度小於128的時候,我們可以用單位元組表示;但如果剩餘長度大於等於128,我們就要用雙位元組甚至更多位元組表示。
舉幾個例子:
剩餘長度 | 十六進位制表示 |
---|---|
38 | 26 |
106 | 6A |
128 | 80 01 |
159 | 9F 01 |
300 | AC 02 |
剩餘長度小於等於128的情況很好理解,直接轉換就是了。但是大於 128 的情況是不是就一頭霧水了,我給大家解釋一下。
一個位元組有 8 位二進位制,最多是 11111111 應該能 256 才對,為什麼單位元組最多表示 127 呢?
這是因為剩餘長度是使用變長度編碼方案,低 7 位(第 0 到 6 位)用於編碼資料,最高有效位(第 7 位)用於表示是否有更多位元組,最大可以使用 4 個位元組。
所以第 0 到 6 位,最多隻能表示到 127。
第 7 位為 0,表示後面沒有位元組了;第 7 位為 1,表示後面還有位元組。位元組遵循低位在前,高位在後。
位元組數 | 最小值 | 最大值 | 最後一個位元組數值1表示 |
---|---|---|---|
1 | 0(0x00) | 127(0x7F) | 1 |
2 | 128(0x80,0x01) | 16383(0xFF,0x7F) | 128 |
3 | 16384(0x80,0x80,0x01) | 2097151(0xFF,0xFF,0x7F) | 16384 |
4 | 2097152(0x80,0x80,0x80,0x01) | 268435455(0xFF,0xFF,0xFF,0x7F) | 2097152 |
舉個例子,159 = 128*1 +31。
所以低八位為 31 並且低八位的第 7 位需要置 1,為 159(0x9F),高八位為 1(0x01)。
剩餘長度 159 的十六進位制就表示為 9F 01。
最後,讓我們來補齊拼圖的最後一塊吧!
完整 CONNECT 報文:10 9F 01 00 04 4D 51 54 54 04 C2 00 64 00 05 74 65 73 74 31 00 0A 4C 31 34 46 43 43 33 38 70 71 00 80 76 65 72 73 69 6F 6E 3D 32 30 31 38 2D 31 30 2D 33 31 26 72 65 73 3D 70 72 6F 64 75 63 74 73 25 32 46 4C 31 34 46 43 43 33 38 70 71 25 32 46 64 65 76 69 63 65 73 25 32 46 74 65 73 74 31 26 65 74 3D 32 30 31 37 38 38 31 37 37 36 26 6D 65 74 68 6F 64 3D 73 68 61 31 26 73 69 67 6E 3D 38 75 6B 46 61 5A 37 74 4B 25 32 42 25 32 42 70 69 73 52 45 55 59 52 4C 59 63 53 69 52 56 77 25 33 44。
就不轉成二進位制了,不然要被 0 和 1 刷屏了哈哈哈哈哈。
7.3.5 CONNECT報文傳送
我們還沒有寫微控制器的程式,所以使用網路除錯助手當作客戶端,展示一下 CONNECT 報文效果。我們首先要知道 OneNET 伺服器地址是 mqtts.heclouds.com : 1883,地址是從 OneNET 文件中心得到的。
開啟網路除錯助手,沒有的同學可以從本文開頭的連結拿。
可以切到 OneNET 看看裝置會顯示線上,100 秒後會斷開,斷了也沒關係,再次傳送 CONNNECT 報文就可以啦。
如果我們寫的 CONNECT 報文不對,收到報文如下並會斷開連線。
那麼有同學可能就好奇了,為什麼 00 是表示連線成功,04表示連線失敗?
因為我們作為客戶端傳送了一條 CONNECT 報文,OneNET 伺服器作為服務端就會返回給我們一條 CONNACK 報文,我們收到的 20 02 00 00 和 20 02 00 04 ,四位位元組就是服務端返回的 CONNACK 報文。關於 CONNACK 報文的詳細介紹在下一節,這裡我先告訴大家如果沒連線成功,怎麼找報文錯誤原因。
“00”、“04” 的意義如下圖,“00” 表示連線已被服務端接受,就是連線成功;“04” 表示使用者名稱或密碼的資料格式無效,可能是有效載荷沒寫對。其它錯誤大家可以根據返回的 CONNACK 報文,找報文錯誤原因。
7.4 CONNACK報文
我們平時點外賣的時候,飯飯送到以後,外賣軟體上需要我們點選確認收貨。同樣的,在 MQTT 中,客戶端傳送 CONNECT 報文,向服務端提出連線請求,服務端返回 CONNACK 報文,確認連線請求。
CONNACK 報文只由固定報頭和可變報頭兩部分組成,沒有有效載荷。
7.4.1 固定報頭
和 CONNECT 報文大同小異,結構如下圖,固定報頭中訊息型別是 2,標誌位是 0000,所以第一位元組是 00100000,轉成十六進位制是 20,剩餘長度為2(後面只有兩位元組的可變報頭),所以 CONNACK 報文固定報頭的十六進位制是:20 02。
7.4.2 可變報頭
CONNACK 報文的可變報頭比 CONNECT 報文簡單很多,只由連線確認標誌和連線返回碼構成,結構如下圖,× 是表示不固定,可能是0,也可能是1:
連線確認標誌的位 7-1 是保留位且必須設定為 0。第 0 (SP)位是當前會話(Session Present)標誌,如果沒有儲存的會話狀態或收到清理會話命令則為 0 ,如果收到儲存會話則為 1 。
連線返回碼表示連線結果,有以下幾種情況:
可變報頭的十六進位制都是未知,所以是:XX XX。
7.4.3 組裝CONNACK報文
CONNACK 報文是不是簡單很多啦,我就不提問了,直接公佈答案:20 02 XX XX。如上面所述,連線正常的話返回值是 20 02 00 00 。
7.5 SUBSCRIBE報文
SUBSCRIBE 報文是用於客戶端請求訂閱主題。訂閱某主題之後,所有釋出的該主題的訊息我們都可以收到。
一條 SUBSCRIBE 報文是以固定報頭、可變報頭、有效載荷三部分組成。
7.5.1 固定報頭
固定報頭和之前的報文大同小異,由兩個位元組組成,結構如下。固定報頭中訂閱訊息型別是 8 ,保留位必須是 0010,所以第一位元組是 10000010,轉成十六進位制是 82,剩餘長度未知,所以 SUBSCRIBE 報文固定報頭的十六進位制是:82 XX。
7.5.2 可變報頭
可變報頭也由兩個位元組組成,結構如下,設定了報文識別符號,相對於給報文起個名字。下圖來自 MQTT 官方文件,它的示例是報文識別符號是 10,大家可以設成自己喜歡的,也可以按照示例,我按照示例來講解,於是轉成十六進位制是 00 0A。
7.5.3 有效載荷
有效載荷由主題過濾器和服務質量要求兩部分組成,結構如下圖。
主題過濾器可在如下頁面找到,我們選擇 $sys/L14FCC38pq/{device-name}/thing/property/set 作為例子訂閱,服務質量要求為0。
要把 {device-name} 換成裝置名,所以,字串 $sys/L14FCC38pq/test1/thing/property/set 轉成十六進位制是:24 73 79 73 2F 4C 31 34 46 43 43 33 38 70 71 2F 74 65 73 74 31 2F 74 68 69 6E 67 2F 70 72 6F 70 65 72 74 79 2F 73 65 74。
別忘了,有效載荷的欄位要求以長度為字首,共 40 個字元,所以是:00 28。
服務質量要求為0,則二進位制是 00000000,轉成十六進位制是:00。
於是,有效荷載 = 長度 + 主題過濾器 + 服務質量要求 = 00 28 24 73 79 73 2F 4C 31 34 46 43 43 33 38 70 71 2F 74 65 73 74 31 2F 74 68 69 6E 67 2F 70 72 6F 70 65 72 74 79 2F 73 65 74 00。
7.5.4 組裝SUBSCRIBE報文
課堂提問時間:)
由上面的講解我們可知:
- 一條 SUBSCRIBE 報文是以固定報頭、可變報頭、有效載荷三部分組成。
- SUBSCRIBE 報文固定報頭的十六進位制是:82 XX。
- SUBSCRIBE 報文的可變頭的十六進位制是:00 0A。
- SUBSCRIBE 報文的有效載荷的十六進位制是:00 28 24 73 79 73 2F 4C 31 34 46 43 43 33 38 70 71 2F 74 65 73 74 31 2F 74 68 69 6E 67 2F 70 72 6F 70 65 72 74 79 2F 73 65 74 00。
求 SUBSCRIBE 報文。
有了組裝 CONNECT 報文的經驗,XX,剩餘長度是多少大家應該都知道了吧,後面有多少位元組就是多少,45 個,轉成十六進位制就是 2D。
於是乎,完整 SUBSCRIBE 報文:82 2D 00 0A 00 28 24 73 79 73 2F 4C 31 34 46 43 43 33 38 70 71 2F 74 65 73 74 31 2F 74 68 69 6E 67 2F 70 72 6F 70 65 72 74 79 2F 73 65 74 00。
7.5.5 SUBSCRIBE報文傳送
傳送成功就會有 SUBACK 報文返回過來,如下圖。
如果我們傳送的 SUBSCRIBE 報文有錯誤,就會斷開連線。
7.6 SUBACK報文
SUBACK,訂閱確認,報文是用於服務端回覆客戶端表示自己收到了 SUBSCRIBE 報文。和 CONNACK 報文一樣,類似於外賣送到了確認收貨的動作。
一條 SUBACK 報文由固定報頭、可變報頭、有效載荷三部分組成。前面以及介紹了三條報文了,大家應該對固定報頭、可變報頭、有效載荷都很熟悉了,接下來我就不囉嗦了,我們直接上乾貨!
7.6.1 固定報頭
結構如下圖,固定報頭中訊息型別是 9,標誌位是 0000,所以第一位元組是 10010000,轉成十六進位制是 90,剩餘長度為3(後面固定有三位元組),所以 CONNACK 報文固定報頭的十六進位制是:90 03。
7.6.2 可變報頭
還記得 SUBSCRIBE 報文的可變報頭嗎,只有兩個位元組,定義了報文識別符號,就是給報文取了個名字。到 SUBACK 報文服務端就需要返回一模一樣的報文識別符號,告訴客戶端,是這個名字的報文被我確認了。我們剛剛的例子報文識別符號是 00 0A,所以這裡也是 00 0A。
7.6.3 有效載荷
有效載荷只有一位元組的返回碼,我們傳送的服務質量等級是多少,返回碼就是多少。
允許的返回碼值:
- 0x00 - 最大 QoS 0
- 0x01 - 成功 – 最大 QoS 1
- 0x02 - 成功 – 最大 QoS 2
- 0x80 - Failure 失敗
上一次傳送我們設定的服務質量等級是 00,返回了 00。如果我們服務質量等級改成 01,那麼 SUBACK 最後一位將返回了 01。
7.6.4 組裝SUBACK報文
so easy,上圖就是答案了:90 03 00 0A XX。
7.7 UNSUBSCRIBE報文
UNSUBSCRIBE 報文,客戶端向服務端傳送,用於取消訂閱主題,取消訂閱某主題後,就不會收到該主題的新訊息了。一條 UNSUBSCRIBE 報文是以固定報頭、可變報頭、有效載荷三部分組成。
7.7.1 固定報頭
結構如下圖,固定報頭中訊息型別是 10,標誌位是 0010,所以第一位元組是 10100010,轉成十六進位制是 A2,剩餘長度未知,所以 UNSUBSCRIBE報文固定報頭的十六進位制是:A2 XX。
7.7.2 可變報頭
老朋友了,報文識別符號,大家可以隨意取名字,這裡我們就使用:00 0B。
7.7.3 有效載荷
有效載荷只有一個部分,那就是你想要取消訂閱的主題,以長度為字首。這裡和 SUBSCRIBE 報文不一樣的是沒有了服務質量要求
我們取消訂閱剛剛訂閱的 $sys/L14FCC38pq/test1/thing/property/set 。
轉成十六進位制,加上長度字首是:00 28 24 73 79 73 2F 4C 31 34 46 43 43 33 38 70 71 2F 74 65 73 74 31 2F 74 68 69 6E 67 2F 70 72 6F 70 65 72 74 79 2F 73 65 74。
7.7.4 組裝UNSUBSCRIBE報文
不多逼逼。
固定報頭:A2 XX
可變報頭:00 0B
有效載荷:00 28 24 73 79 73 2F 4C 31 34 46 43 43 33 38 70 71 2F 74 65 73 74 31 2F 74 68 69 6E 67 2F 70 72 6F 70 65 72 74 79 2F 73 65 74
剩餘長度 XX:2C
完整 UNSUBSCRIBE 報文:A2 2C 00 0B 00 28 24 73 79 73 2F 4C 31 34 46 43 43 33 38 70 71 2F 74 65 73 74 31 2F 74 68 69 6E 67 2F 70 72 6F 70 65 72 74 79 2F 73 65 74。
7.7.5 UNSUBSCRIBE報文傳送
傳送效果如下:
7.8 UNSUBACK報文
UNSUBACK報文,取消訂閱確認,是用於服務端回覆客戶端表示自己收到了 UNSUBSCRIBE 報文。
一條 UNSUBACK 報文由固定報頭、可變報頭兩部分組成。
7.8.1 固定報頭
結構如下圖,固定報頭中訊息型別是 11,標誌位是 0000,所以第一位元組是 10110000,轉成十六進位制是 B0,剩餘長度為 2(後面固定有兩位元組),所以 UNSUBACK 報文固定報頭的十六進位制是:B0 02。
7.8.2 可變報頭
還記得 UNSUBSCRIBE 報文的可變報頭嗎,只有兩個位元組,定義了報文識別符號,就是給報文取了個名字。到 UNSUBACK 報文服務端就需要返回一模一樣的報文識別符號,告訴客戶端,是這個名字的報文被我確認了。我們剛剛的例子是 00 0B,所以這裡也是 00 0B。
7.8.4 組裝UNSUBACK報文
B0 02 00 0B,看看是不是和我們剛剛效果圖上的一樣。
7.9 PUBLISH報文
PUBLISH 報文,釋出訊息,是雙向的,既可以客戶端到服務端,也可以服務端到客戶端。但是需要注意的是釋出訊息只能在同一產品 ID下進行,不能進行跨產品訊息推送。
7.9.1 固定報頭
結構如下圖,固定報頭中訊息型別是 3 ,標誌位是未知,剩餘長度未知。
標誌位填寫規則如下:
- DUP:訊息第一次傳送為 0 ,如果重發為 1 。若 QoS = 0 則 DUP 必須為 0,畢竟 QoS = 0 沒有請求重發。
- QoS 等級:QoS = 0 則 為 00;如果是 01,則 QoS = 1;如果是 10,則 QoS = 2。
- RETAIN:服務端傳送 PUBLISH 報文給客戶端時,如果訊息是作為客戶端一個新訂閱的結果傳送,報文的保留標誌設為 1。當一個 PUBLISH 報文傳送給客戶端是因為匹配一個已建立的訂閱時,服務端必須將保留標誌設為 0,不管它收到的這個訊息中保留標誌的值是多少。如果客戶端發給服務端的 PUBLISH 報文的保留標誌位 0,服務端不能儲存這個訊息也不能移除或替換任何現存的保留訊息。是不是聽不懂,沒關係,我直接告訴大家,這裡設成 0 就行了。
作為例子,我讓 QoS = 0,於是 PUBLISH 報文固定報頭的十六進位制是:30 XX。
7.9.2 可變報頭
可變報頭由主題名和報文識別符號兩部分組成。
主題名(即主題)用於識別有效載荷應該被髮布到哪一個資訊通道,必須是 UTF-8 編碼的格式,也是需要以長度為字首。只有當 QoS = 1 或 QoS = 2 時,報文識別符號才能出現在 PUBLISH 報文中。因為 QoS0 不需要接收端返回報文,所以也就不用指明是哪條報文,也就不用給報文取名字了。
下圖是官方文件的示例,可以參考一下,我們的例子不是這樣的。
這裡我們選擇下面紅框中的主題,直連裝置上報屬性。
$sys/L14FCC38pq/test1/thing/property/post 轉成十六進位制是:24 73 79 73 2F 4C 31 34 46 43 43 33 38 70 71 2F 74 65 73 74 31 2F 74 68 69 6E 67 2F 70 72 6F 70 65 72 74 79 2F 70 6F 73 74。共 41 個字元,加上長度字首 00 29,可變報頭為:00 29 24 73 79 73 2F 4C 31 34 46 43 43 33 38 70 71 2F 74 65 73 74 31 2F 74 68 69 6E 67 2F 70 72 6F 70 65 72 74 79 2F 70 6F 73 74。
7.9.3 有效載荷
我們要釋出的內容就是有效載荷,其格式必須是 JSON 格式,也要轉為十六進位制。
注意:這裡不需要以長度為字首了,因為是 JSON 格式,不是 UTF-8。
JSON格式規則如下:
- 陣列用方括號
[]
表示。 - 物件用大括號
{}
表示。 - 名稱/值對組合成陣列和物件。
- 名稱置於雙引號中,值有字串、數值、布林值、null、物件和陣列。
- 並列的資料之間用逗號
,
分隔
示例如下:
{
"id": "123",
"version": "1.0",
"params": {
"Power": {
"value": "12345",
"time": 1599534283111
},
"temp": {
"value": 23.6,
"time": 1599534283111
}
}
}
我們就簡單點,給 test1 的物模型“收到資料”釋出 6 個 6 ,要使用識別符號 receive 。這個 receive 識別符號就是我們在建立產品的時候所設定的屬性,還記得嗎?
對應的 JSON 格式包如下:
{
"id":"1386772172",
"version":"1.0",
"params":{
"receive":{
"value":"666666"
}
}
}
轉換十六進位制時要把換行符號和 tab 去掉,於是,{"id":"1386772172","version":"1.0","params":{"receive":{"value":"666666"}}} 轉成十六進位制是:7B 22 69 64 22 3A 22 31 33 38 36 37 37 32 31 37 32 22 2C 22 76 65 72 73 69 6F 6E 22 3A 22 31 2E 30 22 2C 22 70 61 72 61 6D 73 22 3A 7B 22 72 65 63 65 69 76 65 22 3A 7B 22 76 61 6C 75 65 22 3A 22 36 36 36 36 36 36 22 7D 7D 7D。
7.9.4 組裝PUBLISH報文
不多逼逼。
固定報頭:30 XX
可變報頭:00 29 24 73 79 73 2F 4C 31 34 46 43 43 33 38 70 71 2F 74 65 73 74 31 2F 74 68 69 6E 67 2F 70 72 6F 70 65 72 74 79 2F 70 6F 73 74
有效載荷:7B 22 69 64 22 3A 22 31 33 38 36 37 37 32 31 37 32 22 2C 22 76 65 72 73 69 6F 6E 22 3A 22 31 2E 30 22 2C 22 70 61 72 61 6D 73 22 3A 7B 22 72 65 63 65 69 76 65 22 3A 7B 22 76 61 6C 75 65 22 3A 22 36 36 36 36 36 36 22 7D 7D 7D
剩餘長度 XX:76
完整PUBLISH報文:30 76 00 29 24 73 79 73 2F 4C 31 34 46 43 43 33 38 70 71 2F 74 65 73 74 31 2F 74 68 69 6E 67 2F 70 72 6F 70 65 72 74 79 2F 70 6F 73 74 7B 22 69 64 22 3A 22 31 33 38 36 37 37 32 31 37 32 22 2C 22 76 65 72 73 69 6F 6E 22 3A 22 31 2E 30 22 2C 22 70 61 72 61 6D 73 22 3A 7B 22 72 65 63 65 69 76 65 22 3A 7B 22 76 61 6C 75 65 22 3A 22 36 36 36 36 36 36 22 7D 7D 7D
7.9.5 PUBLISH報文傳送
效果展示,噹噹噹:
7.10 PINGREQ報文和PINGRESP報文
PINGREQ 報文和 PINGRESP 報文是心跳請求和心跳響應,是 MQTT 心跳機制的兩個報文。客戶端定時向服務端傳送心跳請求(PINGREQ),告訴服務端,我還和你連線著哦。服務端收到心跳請求後,會回覆一條心跳響應(PINGRESP),告訴客戶端,我知道你還連著我啦。
透過心跳機制,MQTT 可以實時監測客戶端的連線狀態,及時發現和處理異常情況,確保通訊的可靠性和穩定性。就像我們依賴心臟維持身體的正常運轉一樣,MQTT的心跳機制也是保障通訊鏈路順暢執行的重要機制之一。
7.10.1 固定報頭
PINGREQ 報文和 PINGRESP 報文都很簡單,只有固定報頭。
PINGREQ 報文,二進位制:1100 0000 0000 0000,十六進位制:C0 00。
PINGRESP 報文,二進位制:1100 0000 0000 0000,十六進位制:D0 00。
7.10.2 PINGREQ報文和PINGRESP報文傳送
報文小結
我們用到的 MQTT 報文都總結在下面啦,不想做筆記的同學可以直接copy。表中的 L 是長度字首,是兩位字元噢,剩餘長度是一位字元。
名稱 | 報文流動方向 | 描述 | 公式 |
---|---|---|---|
CONNECT | 客戶端到伺服器 | 客戶端請求連線到伺服器 | 10+剩餘長度+00 04 4D 51 54 54 04 C2+保持連線時間+L+裝置 ID+L+產品 ID+L+token |
CONNACK | 伺服器到客戶端 | 連線確認 | 20 02+返回碼 |
SUBSCRIBE | 客戶端到伺服器 | 客戶端請求訂閱 | 82+剩餘長度+識別符號+L+主題過濾器+QoS |
SUBACK | 伺服器到客戶端 | 訂閱確認 | 90 03+識別符號+返回碼 |
PUBLISH | 雙向 | 釋出訊息 | 30 +剩餘長度+L+主題+資料(JSON) |
UNSUBSCRIBE | 客戶端到伺服器 | 請求取消訂閱 | A2+剩餘長度+識別符號+主題 |
UNSUBACK | 伺服器到客戶端 | 取消訂閱確認 | B0 02+識別符號 |
PINGREQ | 客戶端到伺服器 | 心跳請求 | C0 00 |
PINGRESP | 伺服器到客戶端 | 心跳響應 | D0 00 |
總結
透過本文的學習,相信你已經對 MQTT 有了一定深度的瞭解和認識。希望這些知識對你在物聯網領域的學習和實踐有所幫助。感謝各位看官,love and peace!
更多關於 MQTT 的細節可以看官方文件。
- 官方文件 3.1.1 中文翻譯下載
連結:https://pan.baidu.com/s/1ya3_WSJDjU5lMjTTxbgF1g?pwd=s3aw 提取碼:s3aw
- 官方文件 5.0 中文翻譯下載
連結:https://pan.baidu.com/s/1xHbdXA5i9fwYduGBn_CJ6g?pwd=r367 提取碼:r367
更多關於 OneNET 的細節也可以看官方開發文件。
OneNET - 中國移動物聯網開放平臺 (10086.cn)
另外,想進大廠的同學,一定要好好學演算法,這是面試必備的。這裡準備了一份 BAT 大佬總結的 LeetCode 刷題寶典,很多人靠它們進了大廠。
刷題 | LeetCode演算法刷題神器,看完 BAT 隨你挑!
有收穫?希望老鐵們來個三連擊,給更多的人看到這篇文章
推薦閱讀:
歡迎關注我的部落格:良許嵌入式教程網,滿滿都是乾貨!