在構(gòu)建物聯(lián)網(wǎng)終端設(shè)備時,通信協(xié)議的選擇直接決定系統(tǒng)的穩(wěn)定性與擴展性。LuatOS通過內(nèi)置MQTT客戶端支持,使開發(fā)者能以極少代碼實現(xiàn)設(shè)備上云。本文將從協(xié)議原理到代碼實現(xiàn),全面解析基于LuatOS的MQTT通信架構(gòu),涵蓋連接、訂閱、發(fā)布、重連等完整流程。
一、基于TCP/IP初步認(rèn)識MQTT
今天我們講的LuatOS MQTT是LuatOS開發(fā)中最常用到的網(wǎng)絡(luò)應(yīng)用之一,用戶使用LuatOS MQTT開發(fā)網(wǎng)絡(luò)應(yīng)用,會接觸到TCP/SSL/認(rèn)證等一些網(wǎng)絡(luò)核心概念;
而說到網(wǎng)絡(luò)應(yīng)用,就繞不開TCP/IP;
所以在本章節(jié)中,我們以MQTT為切入點,先來簡單的回顧復(fù)習(xí)一下TCP/IP協(xié)議的核心知識;
1.1 MQTT在TCP/IP協(xié)議中的位置

MQTT是一個應(yīng)用層的協(xié)議,不考慮TLS安全協(xié)議的情況下:
MQTT可以運行在TCP承載之上,也可以運行在UDP承載之上,也可以運行在WebSocket承載之上;
其中,運行在TCP承載之上的MQTT最為常見,LuatOS MQTT僅支持TCP承載;
所以我們在本講課程中,僅僅介紹運行在TCP承載之上的MQTT;
在MQTT和TCP之間,根據(jù)是否支持TLS加密認(rèn)證來劃分,又可以分為非加密的MQTT和加密的MQTT兩大類;
加密的MQTT根據(jù)支持的身份認(rèn)證類型,又可以分為以下三類:
1、不支持身份認(rèn)證的加密MQTT;
2、支持單向身份認(rèn)證的加密MQTT,client驗證server的身份;
3、支持雙向身份認(rèn)證的加密MQTT,client和server互相驗證對方的身份;
再往下看數(shù)據(jù)鏈路層,支持以太網(wǎng),WiFi,4G等常見的數(shù)據(jù)鏈路承載,對于MQTT應(yīng)用來說,并不關(guān)心數(shù)據(jù)鏈路層的承載是什么類型,只關(guān)心有一個可用的數(shù)據(jù)鏈路網(wǎng)絡(luò)就行;
LuatOS軟件支持4G,以太網(wǎng),WiFi三種數(shù)據(jù)鏈路承載網(wǎng)絡(luò),并且也支持三種網(wǎng)絡(luò)之間按照配置的優(yōu)先級無感自動切換。
1.2 MQTT應(yīng)用數(shù)據(jù)在TCP/IP數(shù)據(jù)包中的位置

上面這張圖演示的是,應(yīng)用層以MQTT為例,TCP/IP數(shù)據(jù) 封裝/解封裝的過程,一個完整的TCP/IP數(shù)據(jù)包如下圖所示:

1、在數(shù)據(jù)發(fā)送端的應(yīng)用層,MQTT網(wǎng)絡(luò)協(xié)議生成MQTT應(yīng)用數(shù)據(jù);
2、MQTT應(yīng)用數(shù)據(jù)向下傳,交給傳輸層,增加了傳輸層的頭部信息,在頭部信息中,有很多字段,其中有兩個字段分別是源端口號和目標(biāo)端口號,這兩個端口號的作用是:定義了發(fā)送端和接收端的應(yīng)用程序,例如MQTT服務(wù)器,如果提供未加密的服務(wù),端口號一般為1883;如果提供加密的服務(wù),端口號一般為8883;通過端口號,可以提供兩個主機之上端到端的通信;
3、傳輸層頭部+MQTT應(yīng)用數(shù)據(jù)向下傳,交給網(wǎng)絡(luò)層,增加了網(wǎng)絡(luò)層的頭部信息,在頭部信息中,有很多字段,其中有兩個字段分別是源IP地址和目標(biāo)IP地址,這兩個IP地址的作用是:定義了發(fā)送端和接收端的網(wǎng)絡(luò)設(shè)備地址;可以提供主機到主機的通信;
4、網(wǎng)絡(luò)層頭部+傳輸層頭部+MQTT應(yīng)用數(shù)據(jù)向下傳,交給數(shù)據(jù)鏈路層,增加了數(shù)據(jù)鏈路層的頭部信息;在這一層會根據(jù)不同的數(shù)據(jù)鏈路承載(例如4G網(wǎng)絡(luò),WiFi網(wǎng)絡(luò),以太網(wǎng)),增加不同的數(shù)據(jù)鏈路層頭部;
5、最終,數(shù)據(jù)鏈路層頭部+網(wǎng)絡(luò)層頭部+傳輸層頭部+MQTT應(yīng)用數(shù)據(jù)的數(shù)據(jù)包,通過物理層轉(zhuǎn)換為0和1的電信號,然后通過物理傳輸介質(zhì)發(fā)給了接收端;
6、接收端收到數(shù)據(jù)后,從下到上,依次解析并且剝離出數(shù)據(jù)鏈路層頭部、網(wǎng)絡(luò)層頭部、傳輸層頭部,最終將MQTT應(yīng)用數(shù)據(jù)交給MQTT應(yīng)用程序去處理;
7、接收端處理完應(yīng)用數(shù)據(jù)后,如果需要返回應(yīng)用數(shù)據(jù)給發(fā)送端;此時發(fā)送端和接收端的角色互換,再走一遍數(shù)據(jù)分層的封裝和解封裝過程;
接下來創(chuàng)建一個MQTT客戶端,去連接地址為"lbsmqtt.airm2m.com",端口為1884的MQTT服務(wù)器(這是一個不能商用的測試服務(wù)器),抓取了完整的MQTT CONNECT和MQTT CONNACK報文,我們來實際分析一下這兩種報文,加深一下對MQTT應(yīng)用的完整TCP/IP數(shù)據(jù)包的理解;
接下來我們打開這兩張圖片看一下:


講到這里,我們對MQTT在TCP/IP協(xié)議棧中的層次位置應(yīng)該有了一個初步的認(rèn)識;
接下來,我們重點理解下MQTT協(xié)議本身的一些知識點;
二、詳細(xì)理解MQTT
2.1 MQTT是什么
MQTT協(xié)議全稱為Message Queuing Telemetry Transport,中文名稱為消息隊列遙測傳輸協(xié)議;
MQTT是一種輕量級、簡單高效的數(shù)據(jù)傳輸協(xié)議,特別適用于物聯(lián)網(wǎng)(IoT)場景下的受限環(huán)境。
2.2 MQTT 核心架構(gòu):發(fā)布 - 訂閱(Publish/Subscribe)模型
2.2.1 三個核心角色
MQTT 采用發(fā)布 / 訂閱(Publish/Subscribe)架構(gòu),包含三個核心角色:

● 發(fā)布者(Publisher):發(fā)送消息的設(shè)備或應(yīng)用
● 代理服務(wù)器(Broker):消息中樞,負(fù)責(zé)接收和分發(fā)消息
● 訂閱者(Subscriber):接收特定主題消息的設(shè)備或應(yīng)用
我們可以看到,消息將發(fā)布者、代理服務(wù)器、訂閱者三個核心角色聯(lián)系到了一起,消息主要由主題(Topic)和承載(Payload)構(gòu)成;
承載比較好理解,就是消息中攜帶的具體的業(yè)務(wù)數(shù)據(jù);
接下來我們重點理解一個概念:主題(Topic);
Topic 是 MQTT 中 “消息分類” 的核心,類似 “郵箱地址”,發(fā)布者向指定 Topic 發(fā)消息,訂閱者通過訂閱 主題 收消息;其設(shè)計有 3 個關(guān)鍵規(guī)則:
1、 層級結(jié)構(gòu):用 “/” 分隔
主題 采用 “樹形層級”,便于按場景分類,例如:
sensor/room1/temp:房間 1 的溫度傳感器數(shù)據(jù);
sensor/room1/hum:房間 1 的濕度傳感器數(shù)據(jù);
device/light/room2:房間 2 的燈光設(shè)備指令。
2、通配符:支持靈活訂閱多個 主題
? 訂閱者可使用 “通配符” 訂閱一類 Topic,無需逐個訂閱,支持兩種通配符:
單級通配符(+):匹配 “一個層級” 的任意內(nèi)容,需放在層級中間或末尾。示例:訂閱sensor/+/temp,可接收sensor/room1/temp、sensor/room2/temp,但無法接收sensor/room1/hum或sensor/room1/temp/real。
多級通配符(#):匹配 “當(dāng)前層級及以下所有層級”,必須放在 Topic 末尾(否則無效)。示例:訂閱sensor/room1/#,可接收sensor/room1/temp、sensor/room1/hum、sensor/room1/temp/real等所有以sensor/room1/開頭的 Topic。
3、注意事項
Topic 區(qū)分大小寫(如Sensor/Room1與sensor/room1是兩個不同 Topic);
Topic 不能以 “/” 開頭(如/sensor/room1不推薦,可能導(dǎo)致層級混亂);
不支持空格和特殊字符(建議使用字母、數(shù)字、/、+、#、_)。
2.2.2 發(fā)布/訂閱架構(gòu)的核心流程
理解了消息,主題(Topic),發(fā)布者(Publisher),代理服務(wù)器(Broker),訂閱者(Subscriber)這幾個概念之后,我們再結(jié)合下面這張表格和圖,理解一下 發(fā)布/訂閱 架構(gòu):

我們以“溫濕度傳感器設(shè)備→手機端APP” 為例,再理解一下發(fā)布/訂閱架構(gòu)的核心流程;
整個通信過程可拆解為 4 個步驟,完全圍繞 Broker 展開,發(fā)布者和訂閱者全程 “無感知”:
1、訂閱者發(fā)起訂閱:手機 APP(訂閱者)與 Broker 建立 MQTT 連接,發(fā)送 “訂閱請求”,明確要訂閱的主題(如sensor/room1/temp)。Broker 收到后,在本地 “訂閱列表” 中記錄:sensor/room1/temp主題對應(yīng)該 手機APP。
2、發(fā)布者發(fā)布消息:溫濕度傳感器設(shè)備(發(fā)布者)與 Broker 建立MQTT 連接,發(fā)送 “發(fā)布請求”,包含主題(sensor/room1/temp)和消息內(nèi)容(“26℃”)。發(fā)布者發(fā)送后無需等待反饋,可繼續(xù)執(zhí)行其他任務(wù)。
3、Broker 匹配并轉(zhuǎn)發(fā):Broker 接收消息后,立即查詢 “訂閱列表”,發(fā)現(xiàn)手機 APP 訂閱了sensor/room1/temp主題。隨后,Broker 將 “26℃” 消息封裝成 “轉(zhuǎn)發(fā)報文”,發(fā)送給該手機 APP。
4、訂閱者接收消息:手機 APP 收到 Broker 轉(zhuǎn)發(fā)的消息,解析后在手機APP界面上顯示 “房間 1 溫度:26℃”,完成一次通信。
2.2.3 發(fā)布/訂閱架構(gòu)的核心優(yōu)勢(為什么物聯(lián)網(wǎng)首選)
相比傳統(tǒng) “點對點(P2P)” 通信(如設(shè)備直接互相連接),發(fā)布/訂閱 架構(gòu)的優(yōu)勢完全貼合物聯(lián)網(wǎng)場景需求:
1、徹底解耦設(shè)備關(guān)系:發(fā)布者和訂閱者無需知道對方的 IP、端口等信息,甚至無需同時在線(如發(fā)布者發(fā)送消息時訂閱者離線,Broker 可保存消息,待訂閱者重連后轉(zhuǎn)發(fā))。例如,一個傳感器的數(shù)據(jù)可同時發(fā)給手機 APP、云端數(shù)據(jù)庫、本地顯示屏,無需傳感器單獨與三者建立連接。
2、靈活的消息篩選(基于 Topic):通過 “主題(Topic)” 實現(xiàn)消息分類,訂閱者可按需訂閱特定 Topic,只接收關(guān)心的消息。例如,訂閱sensor/+/temp可接收所有房間的溫度數(shù)據(jù),訂閱sensor/room1/#可接收房間 1 的溫度、濕度等所有數(shù)據(jù),無需處理無關(guān)信息。
3、支持大規(guī)模擴展:所有設(shè)備只需連接 Broker,無需兩兩互聯(lián),極大降低了網(wǎng)絡(luò)復(fù)雜度。即使設(shè)備數(shù)量從幾十臺增加到幾十萬臺,只需擴容 Broker(如搭建集群),無需修改設(shè)備的通信邏輯,輕松支撐物聯(lián)網(wǎng) “海量設(shè)備” 場景。
總的來說,MQTT 的發(fā)布/訂閱架構(gòu),本質(zhì)是 “通過 Broker 做中間樞紐”,用 “主題” 做消息篩選,讓發(fā)布者和訂閱者徹底解耦。
這種設(shè)計既降低了設(shè)備通信的復(fù)雜度,又能輕松應(yīng)對物聯(lián)網(wǎng) “多設(shè)備、多場景、大規(guī)?!?的核心需求,因此成為物聯(lián)網(wǎng)消息通信的主流架構(gòu)。
2.3 MQTT如何實現(xiàn)可靠通信
MQTT 實現(xiàn)可靠通信的核心在于其設(shè)計了多層次的保障機制,通過協(xié)議層面的規(guī)范確保消息在不可靠網(wǎng)絡(luò)環(huán)境下(如物聯(lián)網(wǎng)常見的低帶寬、高延遲、頻繁斷連場景)能夠高效、準(zhǔn)確地傳輸。
這些機制主要包括消息質(zhì)量等級(QoS)、會話保持、遺囑消息、心跳檢測、保留消息等,共同構(gòu)成了 MQTT 可靠通信的完整體系。
2.3.1 消息質(zhì)量等級(QoS):按需選擇可靠性
MQTT 定義了 3 級消息質(zhì)量等級(Quality of Service),發(fā)布者可根據(jù)消息的重要性選擇合適的等級,在 “可靠性” 和 “傳輸效率” 之間找到平衡。這是 MQTT 實現(xiàn)可靠通信的最核心機制。
1、 QoS 0:最多一次(At Most Once)
1. 核心邏輯:“發(fā)完即忘”,發(fā)布者發(fā)送消息后不等待確認(rèn),Broker 和訂閱者接收后也不返回確認(rèn)。消息可能丟失,但不會重復(fù)。
2. 實現(xiàn)流程:
發(fā)布者向 Broker 發(fā)送PUBLISH報文(QoS 0 標(biāo)識);
Broker 收到后直接轉(zhuǎn)發(fā)給訂閱者(同樣用 QoS 0);
全程無任何確認(rèn)報文,傳輸效率最高,但可靠性最低。
3. 適用場景:非關(guān)鍵數(shù)據(jù),如實時溫濕度、設(shè)備心跳包(丟失一條不影響整體統(tǒng)計)。

2、QoS 1:至少一次(At Least Once)
1. 核心邏輯:通過 “一次確認(rèn)” 確保消息不丟失,但可能因重傳導(dǎo)致重復(fù)。發(fā)布者需等待 Broker 的確認(rèn),Broker 也需等待訂閱者的確認(rèn)。
2. 實現(xiàn)流程(發(fā)布者→Broker):
發(fā)布者向 Broker 發(fā)送PUBLISH報文(QoS 1 + 唯一消息 ID);
Broker 接收后,立即向發(fā)布者返回PUBACK報文(攜帶相同消息 ID),表示 “已收到消息”;
若發(fā)布者超時未收到PUBACK,則重發(fā)PUBLISH報文(消息 ID 不變),直到收到確認(rèn)。
3. 實現(xiàn)流程(Broker→訂閱者):
Broker 向訂閱者發(fā)送PUBLISH報文(QoS 1 + 唯一消息 ID);
訂閱者接收后返回PUBACK報文(攜帶相同消息 ID),表示 “已收到消息”;
Broker 超時未收到PUBACK,則重發(fā)PUBLISH報文(消息 ID 不變),直到收到確認(rèn)。
4. 適用場景:關(guān)鍵數(shù)據(jù)但可容忍重復(fù)(如設(shè)備告警,重復(fù)可通過消息 ID 去重)。

3、QoS 2:恰好一次(Exactly Once)
1. 核心邏輯:通過 “四步握手” 機制確保消息僅被接收一次,無丟失、無重復(fù),是可靠性最高的等級,但傳輸延遲和資源消耗最大。
2. 實現(xiàn)流程(發(fā)布者→Broker):
發(fā)布者發(fā)送PUBLISH報文(QoS 2 + 消息 ID);
Broker 接收后,返回PUBREC報文(表示 “已接收,準(zhǔn)備處理”);
發(fā)布者收到PUBREC后,發(fā)送PUBREL報文(表示 “允許 Broker 處理并轉(zhuǎn)發(fā)”);
Broker 收到PUBREL后,完成消息處理,返回PUBCOMP報文(表示 “處理完成”),發(fā)布者收到后結(jié)束流程。
3. 實現(xiàn)流程(Broker→訂閱者):同上,通過四步握手確保訂閱者僅接收一次。
4. 適用場景:絕對不能重復(fù) / 丟失的數(shù)據(jù)(如金融交易指令)。

2.3.2 會話保持(Session Persistence):斷連后消息不丟失
MQTT 通過 “會話保持” 機制解決客戶端異常斷連后的消息續(xù)傳問題,確保重連后通信狀態(tài)可恢復(fù)。核心是 Broker 對客戶端會話狀態(tài)的存儲策略,由客戶端連接時的Clean Session參數(shù)控制。
1、持久會話(Clean Session = 0)
1. 核心邏輯:Broker 保存客戶端的會話狀態(tài),包括:
客戶端的訂閱列表(無需重連后重新訂閱);
未完成的 QoS 1/2 消息(如未收到PUBACK的消息);
已發(fā)送但未被確認(rèn)的消息(如 Broker 發(fā)給客戶端但未收到確認(rèn)的消息)。
2. 流程:
客戶端連接時指定Clean Session = 0,Broker 為其創(chuàng)建持久會話;
客戶端斷連后,Broker 保留會話狀態(tài);
客戶端重連(使用相同 Client ID)后,Broker 恢復(fù)訂閱關(guān)系,并補發(fā)未完成的消息。
3. 適用場景:長期在線設(shè)備(如工業(yè)控制器),需確保重連后不丟失消息和訂閱。
2、臨時會話(Clean Session = 1)
1. 核心邏輯:Broker 不保存任何會話狀態(tài),客戶端斷連后,所有訂閱和未完成消息全部清除。
2. 流程:
客戶端連接時指定Clean Session = 1;
斷連后,Broker 立即刪除該客戶端的所有信息;
重連后,客戶端需重新發(fā)送訂閱請求才能接收消息。
3. 適用場景:臨時連接設(shè)備(如手機 APP 偶爾查看數(shù)據(jù)),減少 Broker 存儲壓力。
在嵌入式端,為了保證業(yè)務(wù)邏輯的簡單,一般來說,不開啟會話保持,大家對這個功能了解一下即可。
2.3.3 遺囑消息(Last Will and Testament, LWT):設(shè)備離線狀態(tài)可感知
當(dāng)客戶端異常離線(如斷電、網(wǎng)絡(luò)中斷)時,Broker 會自動向指定主題發(fā)送 “遺囑消息”,通知其他訂閱者該設(shè)備的離線狀態(tài),確保系統(tǒng)對設(shè)備狀態(tài)的感知可靠性。
1、配置流程
1、客戶端1在連接階段(CONNECT報文)向 Broker 聲明遺囑參數(shù):
2、Will Topic:遺囑消息的目標(biāo)主題(如device/room1/status);
3、Will Message:遺囑消息內(nèi)容(如offline);
4、Will QoS:遺囑消息的 QoS 等級(確保遺囑本身可靠送達(dá));
5、Will Retain:是否保留遺囑消息(讓新訂閱者也能收到)。
2、觸發(fā)機制
正常斷開:客戶端1發(fā)送DISCONNECT報文后斷開連接,Broker 不發(fā)送遺囑消息;
異常斷開:Broker 檢測到 TCP 連接異常斷開(如心跳超時、連接被強制關(guān)閉),且未收到DISCONNECT報文,則立即向Will Topic發(fā)送Will Message。所有訂閱了這個Will Topic的其他客戶端,都會收到這條遺囑消息;
3、適用場景
設(shè)備在線狀態(tài)監(jiān)控(如智能家居中 “空調(diào)離線” 時,APP 接收遺囑消息并提示用戶);
故障自動告警(如工業(yè)傳感器離線,監(jiān)控系統(tǒng)接收遺囑消息后觸發(fā)檢修流程)。

2.3.4 心跳檢測(Keepalive):連接狀態(tài)實時監(jiān)控
MQTT 通過 “心跳機制” 確保 Broker 和客戶端能及時檢測到連接異常,避免因網(wǎng)絡(luò)中斷導(dǎo)致的 “假在線” 狀態(tài),為會話恢復(fù)和遺囑消息觸發(fā)提供依據(jù)。
1、工作原理
客戶端連接時指定Keepalive時間(如 60 秒),表示 “客戶端需在每個Keepalive間隔內(nèi)至少發(fā)送一次報文(任何類型,如PUBLISH、PINGREQ)”;
若客戶端長時間未發(fā)送報文,Broker 會主動發(fā)送PINGREQ報文探測客戶端是否在線;
客戶端收到PINGREQ后需回復(fù)PINGRESP報文;
若 Broker 超時未收到PINGRESP(通常為 1.5 倍Keepalive時間),則判定客戶端離線,觸發(fā)遺囑消息(若配置)并關(guān)閉連接。
2、作用
實時檢測連接狀態(tài),避免無效連接占用資源;
為遺囑消息提供準(zhǔn)確的觸發(fā)條件(確保僅在真正離線時發(fā)送)。
因為沒有合適的環(huán)境模擬客戶端不發(fā)送心跳的場景,所以下面這張圖使用LuatOS軟件抓取了一份正常發(fā)送心跳的網(wǎng)絡(luò)包,幫助理解一下心跳機制

2.3.5 保留消息(Retained Message):新訂閱者可獲取歷史數(shù)據(jù)
當(dāng)發(fā)布者發(fā)送消息時,可設(shè)置Retain標(biāo)志為1,Broker 會保存該主題的 “最新保留消息”。當(dāng)新訂閱者訂閱該主題時,Broker 會立即將這條保留消息推送給它,無需等待發(fā)布者再次發(fā)送,確保新訂閱者能獲取歷史數(shù)據(jù)。
1、特點
Broker 僅保存每個主題的最新一條保留消息(新保留消息會覆蓋舊消息);
若發(fā)布者發(fā)送一條空內(nèi)容的保留消息,Broker 會刪除該主題的保留消息;
保留消息與會話無關(guān),即使發(fā)布者離線,新訂閱者仍能收到。
2、適用場景
設(shè)備狀態(tài)同步(如智能燈的 “開關(guān)狀態(tài)”,新訂閱者上線后立即知道當(dāng)前狀態(tài));
歷史數(shù)據(jù)快照(如傳感器的最新讀數(shù),新監(jiān)控設(shè)備接入時無需等待實時數(shù)據(jù))。

2.3.6 總結(jié):MQTT 可靠通信的協(xié)同機制
MQTT 的可靠通信不是單一機制的作用,而是多個機制的協(xié)同:
QoS 等級:從協(xié)議層面確保消息 “不丟失”“不重復(fù)”;
會話保持:解決斷連后消息續(xù)傳和狀態(tài)恢復(fù)問題;
遺囑消息:讓系統(tǒng)感知設(shè)備異常離線;
心跳檢測:實時監(jiān)控連接狀態(tài),為可靠性機制提供判斷依據(jù);
保留消息:確保新訂閱者能獲取歷史數(shù)據(jù)。
這些機制共同構(gòu)成了 MQTT 靈活、可控的可靠性體系,使其既能滿足物聯(lián)網(wǎng) “低帶寬、受限設(shè)備” 的輕量需求,又能支撐可靠性要求極高的場景。
2.4 MQTT的主要業(yè)務(wù)流程
MQTT 的業(yè)務(wù)流程圍繞 “客戶端與 Broker 之間的連接建立、消息交互、連接斷開” 三大階段展開,每個階段包含多個標(biāo)準(zhǔn)化的交互步驟,確保通信的可靠性和規(guī)范性。以下是核心業(yè)務(wù)流程的詳細(xì)拆解:
2.4.1 連接建立階段:客戶端與 Broker 建立通信鏈路
這是所有 MQTT 通信的前提,客戶端(發(fā)布者 / 訂閱者)需先與 Broker 建立 TCP 連接,核心步驟包括:
1、MQTT如果在傳輸層使用的是TCP,則首先MQTT客戶端會和MQTT Broker服務(wù)器建立TCP連接。
2、客戶端發(fā)起連接請求(CONNECT 報文)客戶端向 Broker 發(fā)送CONNECT報文,包含關(guān)鍵信息:
客戶端 ID(Client ID):唯一標(biāo)識客戶端(如 “sensor_room1_001”),Broker 據(jù)此區(qū)分不同設(shè)備;
協(xié)議版本:如 MQTT 5.0、3.1.1;
連接參數(shù):是否保持會話(Clean Session)、心跳間隔(Keepalive,如 60 秒,客戶端需在此間隔內(nèi)發(fā)送報文證明在線);
認(rèn)證信息:可選的用戶名 / 密碼、TLS 證書(用于身份驗證);
遺囑消息參數(shù):若客戶端異常離線,Broker 需發(fā)送的遺囑主題、內(nèi)容、QoS 等級。
3、Broker 確認(rèn)連接(CONNACK 報文)Broker 驗證CONNECT報文后,返回CONNACK報文:
若驗證通過(Client ID 合法、認(rèn)證成功等),返回 “連接成功” 響應(yīng),包含 “會話狀態(tài)”(如是否恢復(fù)之前的持久會話);
若驗證失?。ㄈ?Client ID 重復(fù)、認(rèn)證失敗),返回錯誤碼(如 “拒絕連接”),并關(guān)閉 TCP 連接。
最終結(jié)果:連接建立成功后,客戶端與 Broker 保持 長連接,進入 “消息交互階段”。

2.4.2 消息交互階段:訂閱、取消訂閱、發(fā)布、接收的核心流程
連接建立后,客戶端可執(zhí)行 “訂閱主題”“發(fā)布接收消息”“取消訂閱” 等核心操作,所有交互均通過標(biāo)準(zhǔn)化報文完成:
1、訂閱主題(客戶端 → Broker)
訂閱者需先訂閱感興趣的主題,才能接收對應(yīng)消息:
客戶端發(fā)送 SUBSCRIBE 報文:包含需訂閱的主題列表(如sensor/room1/temp)及每個主題的 QoS 等級(如 QoS 1);
Broker 回復(fù) SUBACK 報文:確認(rèn)訂閱結(jié)果,返回每個主題的 “實際 QoS 等級”(可能低于客戶端請求,由 Broker 根據(jù)自己的服務(wù)能力和安全策略決定)。
示例:手機 APP 發(fā)送SUBSCRIBE訂閱sensor/room1/temp(QoS 1),Broker 返回SUBACK確認(rèn) “訂閱成功,QoS 1”,此后該 APP 將收到該主題的所有消息。

2、取消訂閱主題(客戶端 → Broker)
訂閱者可隨時取消對某個主題的訂閱:
客戶端發(fā)送 UNSUBSCRIBE 報文:包含需取消的主題列表(如sensor/room1/temp);
Broker 回復(fù) UNSUBACK 報文:確認(rèn)取消成功,此后不再向該客戶端轉(zhuǎn)發(fā)對應(yīng)主題的消息。

3、發(fā)布/接收消息(發(fā)布者 → Broker → 訂閱者)
發(fā)布者向指定主題發(fā)送消息,Broker 負(fù)責(zé)轉(zhuǎn)發(fā)給所有訂閱者,流程因 QoS 等級略有差異(以最常用的 QoS 1 為例):
步驟 1:發(fā)布者發(fā)送PUBLISH報文,包含主題(如sensor/room1/temp)、消息內(nèi)容(如 “25℃”)、QoS 1 標(biāo)識;
步驟 2:Broker 接收后,向發(fā)布者回復(fù)PUBACK報文(確認(rèn)已收到消息);
步驟 3:Broker 查詢訂閱列表,向所有訂閱該主題的訂閱者發(fā)送PUBLISH報文(內(nèi)容相同,QoS 等級與訂閱時一致);
步驟 4:每個訂閱者接收后,向 Broker 回復(fù)PUBACK報文(確認(rèn)已收到)。
其他:QoS 0 無確認(rèn)機制(“發(fā)完即忘”),QoS 2 需 “四步握手”(PUBLISH→PUBREC→PUBREL→PUBCOMP),確保消息僅被接收一次。

4、心跳?;睿蛻舳?→ Broker)
客戶端發(fā)送 PINGREQ 報文;
Broker 回復(fù) PINGRESP 報文。

2.4.3 連接斷開階段:正常 / 異常斷開的處理
通信結(jié)束后,客戶端或 Broker 會主動斷開連接,或因異常被動斷開,核心流程包括:
1、正常斷開客戶端發(fā)送DISCONNECT報文給 Broker,明確表示 “主動斷開連接”,Broker 收到后:
關(guān)閉 TCP 連接;
若為持久會話(Clean Session=0),保留客戶端的訂閱列表和未完成消息;
不觸發(fā)遺囑消息(因為是 “正常斷開”)。
2、異常斷開若客戶端因斷電、網(wǎng)絡(luò)中斷等原因異常離線(Broker 未收到DISCONNECT報文,但 TCP 連接斷開或心跳超時),Broker 會:
觸發(fā) “遺囑消息”:按客戶端連接時聲明的參數(shù),向遺囑主題發(fā)送遺囑消息(如device/offline);
清理資源:若為臨時會話(Clean Session=1),刪除該客戶端的所有訂閱和消息;若為持久會話,保留信息直至客戶端重連。

2.4.3 一個實際的MQTT項目業(yè)務(wù)示例
在理解了MQTT連接/斷開連接、訂閱/取消訂閱、發(fā)布/接收的業(yè)務(wù)流程后,我們結(jié)合一個實際的MQTT項目業(yè)務(wù),再來總體理解一下這些核心業(yè)務(wù)

2.5 MQTT協(xié)議報文
MQTT 協(xié)議的所有通信都通過標(biāo)準(zhǔn)化的報文(Packet) 完成,每種報文有明確的格式和用途。MQTT 報文結(jié)構(gòu)統(tǒng)一分為三部分:固定頭(Fixed Header)、可變頭(Variable Header) 和負(fù)載(Payload),其中后兩部分是否存在取決于報文類型。
MQTT報文類型共有14種:

所有 MQTT 報文都遵循以下基本結(jié)構(gòu)(從前往后依次排列):

三、基于MQTT理解LuatOS上的TCP/IP協(xié)議棧
在總體了解了MQTT的核心理論知識之后,接下來我們一起看下LuatOS上對TCP/IP協(xié)議棧的支持情況,以及提供了哪些編程接口給LuatOS項目應(yīng)用腳本來使用,參考下表:

具體到LuatOS MQTT應(yīng)用編程,我們僅需要關(guān)注上表中黃色背景的幾部分:
1、mqtt核心庫:這個庫是LuatOS MQTT應(yīng)用編程的核心,在下一章節(jié)我們會重點講解;
2、socket核心庫、"IP_READY"和"IP_LOSE"消息:在LuatOS MQTT應(yīng)用開發(fā)中,會主動查詢網(wǎng)卡的狀態(tài),或者被動等待網(wǎng)卡狀態(tài)的變化通知,這部分功能就會用到socket核心庫、"IP_READY"和"IP_LOSE"消息;在本講最后我們學(xué)習(xí)LuatOS MQTT client應(yīng)用開發(fā)框架時,再結(jié)合具體的代碼演示如何使用這部分功能;
3、exnetif擴展庫:在LuatOS MQTT應(yīng)用開發(fā)中,會使用到單網(wǎng)卡(4G 、WiFi、以太網(wǎng)網(wǎng)卡中的一種)或者多網(wǎng)卡(4G 、WiFi、以太網(wǎng)網(wǎng)卡中的多種),exnetif擴展庫可以既簡捷又靈活的配置各種網(wǎng)卡,讓MQTT應(yīng)用編程和網(wǎng)卡管理完全解耦;在本講最后我們學(xué)習(xí)LuatOS MQTT client應(yīng)用開發(fā)框架時,我們結(jié)合具體的代碼來分析exnetif擴展庫如何使用;
四、LuatOS上的MQTT核心庫
LuatOS 提供 mqtt 核心庫,可以實現(xiàn) mqtt 客戶端,不支持mqtt server,有以下幾點注意事項:
1、只支持 mqtt 3.1.1,其他版本例如 3.1 或者 5.0 均不支持?。?!
2、只支持mqtt/mqtts over tcp,不支持 mqtt over websocket,不支持mqtt over udp;
3、幾個大前提:
本庫基于 TCP 連接,支持加密 TCP 和非加密 TCP;
任何通信失敗都將斷開連接,如果開啟了自動重連,那么間隔 N 秒后會開始自動重連;
上行數(shù)據(jù)均為一次性的,沒有緩存機制,更沒有上行的重試/重發(fā)機制;
4、重要的事情說 3 次:沒有重發(fā)機制,沒有重發(fā)機制,沒有重發(fā)機制
MQTT 協(xié)議中規(guī)定了重發(fā)機制,但那是云端/服務(wù)器端才會實現(xiàn)的機制,模塊端是沒有的;
上行失敗,唯一的可能性就是 TCP 鏈接出問題了,而 TCP 鏈接出問題的解決辦法就是重連;
模塊端不會保存任何上行數(shù)據(jù),重連后也無法實現(xiàn)重發(fā);
5、QoS僅支持0和1,不支持2;
4.1 常量詳解
核心庫常量,顧名思義是由 LuatOS 內(nèi)核固件中定義的、不可重新賦值或修改的固定值,在腳本代碼中不需要聲明,可直接調(diào)用;

4.1.1 mqtt.STATE_DISCONNECT

4.1.2 mqtt.STATE_SCONNECT

4.1.3 mqtt.STATE_MQTT

4.1.4 mqtt.STATE_READY

4.2 函數(shù)詳解
4.2.1 mqtt.create(adapter, host, port, ssl, isipv6)
功能
mqtt 客戶端創(chuàng)建;
注意事項
1. 返回值有返回 nil 的情況,在調(diào)用后必須進行檢查返回值是否為 nil,且要做邏輯處理;
參數(shù)
adapter

host

port

ssl

isipv6

返回值
local mqttc = mqtt.create(adapter, host, port, ssl, isipv6)
有一個返回值 mqttc
mqttc

示例

4.2.2 mqttc:debug(onoff)
功能
配置是否打開 debug 信息,即控制 mqtt 客戶端是否輸出調(diào)試信息;
注意事項
1. 必須傳入布爾類型參數(shù) onoff(true 表示打開調(diào)試,false 表示關(guān)閉調(diào)試);
2. 建議開發(fā)調(diào)試階段可打開,方便查看 mqtt 通信的詳細(xì)過程。調(diào)試結(jié)束后應(yīng)關(guān)閉,以減少不必要的日志輸出;
參數(shù)
onoff

返回值
該接口無返回值,只需調(diào)用該接口執(zhí)行相關(guān)操作,無需處理返回結(jié)果;
如果一定要把接口調(diào)用的結(jié)果賦值給一個變量,則這個變量就是一個nil值;
示例

4.2.3 mqttc:auth(client_id, username, password, cleanSession)
功能
配置 mqtt 三元組信息及會話清除策略(cleanSession);
注意事項
1. client_id、username、password 有長度限制,其中 client_id 和 username 限制不超過 192 字符,password 限制不超過 512 字符,超過限制會導(dǎo)致配置失??;
2. 相同的 client_id 在同一服務(wù)器上會導(dǎo)致設(shè)備互相踢下線,需要確保 client_id 的唯一性;
參數(shù)
client_id

username

password

cleanSession

返回值
local mqtt_auth_result = mqttc:auth(client_id, username, password, cleanSession)
有一個返回值 mqtt_auth_result
mqtt_auth_result

示例

4.2.4 mqttc:keepalive(time)
功能
設(shè)置 mqtt 客戶端的心跳時間間隔,單位為秒(s);
注意事項
1. 參數(shù) time 為可選值,默認(rèn)值為 240 秒,最小值限制為 15 秒(若傳入小于 15 的值,會被強制設(shè)為 15 秒),最大值限制為 600 秒(若傳入大于 600 的值,會被強制設(shè)為 600 秒);
2. 實際發(fā)送心跳時間間隔 = 設(shè)置的心跳時間間隔 × 0.75,這是系統(tǒng)防止因網(wǎng)絡(luò)問題導(dǎo)致心跳包未在設(shè)定時間到達(dá)服務(wù)器時所執(zhí)行的策略,從而確保了連接的穩(wěn)定性;

3. 建議在 mqtt 連接前設(shè)置心跳參數(shù),也可以在連接后動態(tài)調(diào)整心跳時間;
4. 網(wǎng)絡(luò)穩(wěn)定環(huán)境可使用默認(rèn)值 240 秒,或根據(jù)服務(wù)器要求調(diào)整;
5. 心跳間隔過短會增加功耗和網(wǎng)絡(luò)流量,過長可能導(dǎo)致連接被網(wǎng)絡(luò)斷開;
參數(shù)
time

返回值
該接口無返回值,只需調(diào)用該接口執(zhí)行相關(guān)操作,無需處理返回結(jié)果;
如果一定要把接口調(diào)用的結(jié)果賦值給一個變量,則這個變量就是一個nil值;
示例

4.2.5 mqttc:will(topic, payload, qos, retain)
功能
設(shè)置 mqtt 客戶端的遺囑消息;
注意事項
1. 必須在調(diào)用 mqttc:connect() 之前調(diào)用此接口,否則遺囑消息設(shè)置不會生效;
參數(shù)
topic

payload

qos

retain

返回值
local mqtt_will_result = mqttc:will(topic, payload, qos, retain)
有一個返回值 mqtt_will_result
mqtt_will_result

示例

4.2.6 mqttc:autoreconn(reconnect, reconnect_time)
功能
用于配置 mqtt 客戶端的自動重連功能,包括啟用/禁用自動重連以及設(shè)置重連周期;
注意事項
1. 該接口僅控制自動重連的配置,不直接執(zhí)行重連操作;
2. 重連功能僅在連接斷開后生效,不會主動嘗試斷開現(xiàn)有連接;
3. 如果項目中使用多網(wǎng)卡功能,一定不要使用此接口,否則內(nèi)核固件在自動重連時,可能會使用錯誤的網(wǎng)卡,導(dǎo)致無法上網(wǎng);
參數(shù)
reconnect

reconnect_time

返回值
該接口無返回值,只需調(diào)用該接口執(zhí)行相關(guān)操作,無需處理返回結(jié)果;
如果一定要把接口調(diào)用的結(jié)果賦值給一個變量,則這個變量就是一個nil值;
示例

4.2.7 mqttc:on(cb)
功能
注冊 mqtt 客戶端事件回調(diào)函數(shù)的接口;
注意事項
1. 回調(diào)函數(shù)應(yīng)保持簡潔高效 ,避免執(zhí)行耗時操作(如復(fù)雜計算、阻塞 IO 等),否則可能影響 mqtt 客戶端的消息處理實時性;
參數(shù)
cb

返回值
該接口無返回值,只需調(diào)用該接口執(zhí)行相關(guān)操作,無需處理返回結(jié)果;
如果一定要把接口調(diào)用的結(jié)果賦值給一個變量,則這個變量就是一個nil值;
示例

4.2.8 mqttc:connect()
功能
連接 mqtt 服務(wù)器;
注意事項
1. 該接口返回 true 僅表示連接請求成功發(fā)起,不代表與 mqtt 服務(wù)器的連接已成功建立;
2. 該接口返回 false 僅表示連接請求失敗,通常意味著底層 socket 創(chuàng)建或連接失敗;
3. 調(diào)用該接口前必須先配置服務(wù)器地址、端口、客戶端 ID 等參數(shù),如果未配置必要參數(shù),該接口可能返回 false;
參數(shù)
無;
返回值
local mqtt_connect_result = mqttc:connect()
有一個返回值 mqtt_connect_result
mqtt_connect_result

示例

4.2.9 mqttc:ready()
功能
檢查 mqtt 客戶端是否處于就緒狀態(tài)(已成功連接到 mqtt 服務(wù)器);
注意事項
1. 該接口僅檢查客戶端的內(nèi)部狀態(tài)標(biāo)記,不進行實際的網(wǎng)絡(luò)連通性測試;
2. 返回 true 僅表示客戶端當(dāng)前認(rèn)為自己處于連接狀態(tài),不保證網(wǎng)絡(luò)連接一定穩(wěn)定;
3. 即使返回 true,后續(xù)的發(fā)布/訂閱操作仍有可能因網(wǎng)絡(luò)問題失敗,建議做好錯誤處理;
參數(shù)
無;
返回值
local mqtt_ready_result = mqttc:ready()
有一個返回值 mqtt_ready_result
mqtt_ready_result

示例

4.2.10 mqttc:subscribe(topic, qos)

功能
訂閱一個/多個主題(topic);
注意事項
1. 需要先建立 mqtt 客戶端且連接成功后再調(diào)用;
2. 訂閱時 topic 的命名要合法,qos 的值要在規(guī)定范圍內(nèi);
3. 多主題訂閱下,只要有任一主題訂閱失敗,整體返回 nil,需注意訂閱失敗的處理邏輯;
參數(shù)
topic

qos

返回值
local mqtt_sub_result = mqttc:subscribe(topic, qos)
有一個返回值 mqtt_sub_result
mqtt_sub_result

示例

4.2.11 mqttc:publish(topic, data, qos, retain)

功能
向指定的 mqtt 主題發(fā)布消息;
注意事項
1. 調(diào)用此接口前,必須確保 mqtt 客戶端已經(jīng)成功連接到服務(wù)器,否則發(fā)布操作可能失??;
2. 遵循 mqtt 主題命名規(guī)范,避免使用特殊字符導(dǎo)致的兼容性問題;
參數(shù)
topic

data

qos

retain

返回值
local mqtt_pub_result = mqttc:publish(topic, data, qos, retain)
有一個返回值 mqtt_pub_result
mqtt_pub_result

示例

4.2.12 mqttc:unsubscribe(topic)

功能
取消訂閱主題(Topic);
注意事項
1. 需要確保取消訂閱的主題存在,否則會取消訂閱失敗;
2. 多主題取消訂閱下,只要有任一主題取消訂閱失敗,整體返回 nil,需注意處理;
參數(shù)
topic

返回值
該接口無返回值,只需調(diào)用該接口執(zhí)行相關(guān)操作,無需處理返回結(jié)果;
如果一定要把接口調(diào)用的結(jié)果賦值給一個變量,則這個變量就是一個nil值;
示例

4.2.13 mqttc:disconnect()

功能
用于斷開與 mqtt 服務(wù)器的連接,但不會釋放 mqtt 客戶端的資源。
注意事項
1. 該接口僅斷開與服務(wù)器的連接,但 不會釋放 mqtt 客戶端實例的資源 (如內(nèi)存、配置等)
參數(shù)
無;
返回值
local mqtt_disconnect_result = mqttc:disconnect()
有一個返回值 mqtt_disconnect_result
mqtt_disconnect_result

示例

4.2.14 mqttc:close()

功能
關(guān)閉 mqtt 客戶端連接并釋放相關(guān)資源;
注意事項
1. 調(diào)用 close() 后,mqtt 客戶端相關(guān)的所有資源(包括套接字、回調(diào)函數(shù)引用等)都會被釋放;
2. 釋放資源后,該 mqtt 客戶端實例將無法再使用,若需要重新連接 mqtt 服務(wù)器,必須創(chuàng)建新的客戶端實例;
3. 此操作是不可逆的,一旦調(diào)用,無法通過任何方式恢復(fù)客戶端的功能;
參數(shù)
無;
返回值
該接口無返回值,只需調(diào)用該接口執(zhí)行相關(guān)操作,無需處理返回結(jié)果;
如果一定要把接口調(diào)用的結(jié)果賦值給一個變量,則這個變量就是一個nil值;
示例

4.2.15 mqttc:state()
功能
獲取 mqtt 客戶端的當(dāng)前狀態(tài),返回一個代表不同連接階段或狀態(tài)的整數(shù)值;
注意事項
1. 狀態(tài)碼僅反映客戶端內(nèi)部的狀態(tài)標(biāo)記,不保證與實際網(wǎng)絡(luò)連接狀態(tài)完全一致;
2. 在使用狀態(tài)碼進行邏輯判斷時,建議使用常量而非硬編碼的數(shù)字,以提高代碼可維護性;
3. 與 mqttc:ready() 類似,即使返回就緒狀態(tài),實際的發(fā)布/訂閱操作仍可能因網(wǎng)絡(luò)問題失敗;
參數(shù)
無;
返回值
local mqtt_state = mqttc:state()
有一個返回值 mqtt_state
mqtt_state

示例

五、LuatOS上的MQTT client 應(yīng)用開發(fā)框架
在理解了mqtt核心庫之后,我們再來實際看一個完整的mqtt client長連接的demo項目代碼,重點分析下如何在項目中使用LuatOS mqttt核心庫;
mqtt client長連接的demo項目代碼路徑:Air8000 mqtt demo;
5.1 總體設(shè)計框圖
demo項目的總體設(shè)計框圖如下:

5.2 模擬器上運行這個項目(使用模擬器單網(wǎng)卡演示項目完整的業(yè)務(wù)邏輯)
首先我們在LuatOS模擬器上運行一下這個demo項目,讓大家對實現(xiàn)的功能有一個直觀的認(rèn)識
如果要在LuatOS模擬器上運行這個項目,按照以下幾點準(zhǔn)備好軟件環(huán)境:
1、netdrv_device.lua中打開pc模擬器的網(wǎng)卡驅(qū)動文件require "netdrv_pc",注釋掉其他其他網(wǎng)卡文件;
2、因為這個demo項目需要用到uart功能,要在pc上模擬uart收發(fā)功能,需要在pc上安裝一個虛擬串口工具,生成一組串口,例如串口1和串口2,如果這兩個串口id在你的pc上已經(jīng)被占用,可以自定義生成任意兩個id的串口就行;
虛擬串口工具生成的這一對串口可以互相給對方發(fā)數(shù)據(jù),也能互相接收對方發(fā)送過來的數(shù)據(jù);
如果生成的是串口1和串口2,這個demo項目中的uart_app.lua中使用的uart1,不用修改uart_app.lua的代碼,此時在pc上使用sscom或者llcom串口工具打開串口2即可,這樣的話,模擬器中的uart1就可以和sscom或者llcom打開的uart2進行收發(fā)數(shù)據(jù);
如果生成的一對串口沒有串口1,假設(shè)是串口11和串口12,則需要修改uart_app.lua中的代碼,串口id修改為11,pc上使用sscom或者llcom串口工具打開串口12即可,這樣的話,模擬器中的uart11就可以和sscom或者llcom打開的uart12進行收發(fā)數(shù)據(jù);
在PC上創(chuàng)建MQTT客戶端,用來:
訂閱以下主題,接收demo中的四個MQTT client發(fā)布的消息:
zhutianhua1/uart/up
zhutianhua1/timer/up
在主題862991234567980/down上發(fā)布消息給demo中的四個MQTT client
軟件環(huán)境準(zhǔn)備好之后,接下來我們在模擬器上實際運行一下這個項目看看效果;
雙擊 cmd 命令行窗口,然后輸入下面一行命令,運行 luatos 批處理文件,同時輸入要運行的 luatos 項目配置文件

然后按回車鍵,就可以運行 mqtt 項目軟件;
5.3 分析項目代碼
這個mqtt demo中的readme文件,以及代碼中的注釋都比較詳細(xì),接下來我用vscode直接打開這份demo項目代碼,和大家一起分析下項目代碼;
5.4 Air8000開發(fā)板上運行演示這個項目(重點演示多網(wǎng)卡的使用)
準(zhǔn)備硬件環(huán)境:
1、Air8000開發(fā)板一塊+可上網(wǎng)的sim卡一張+4g天線一根+wifi天線一根+網(wǎng)線一根:
sim卡插入開發(fā)板的sim卡槽
天線裝到開發(fā)板上
網(wǎng)線一端插入開發(fā)板網(wǎng)口,另外一端連接可以上外網(wǎng)的路由器網(wǎng)口或者交換機網(wǎng)口;
2、TYPE-C USB數(shù)據(jù)線一根 + USB轉(zhuǎn)串口數(shù)據(jù)線一根,Air8000開發(fā)板和數(shù)據(jù)線的硬件接線方式為:
Air8000開發(fā)板通過TYPE-C USB口供電;(外部供電/USB供電 撥動開關(guān) 撥到 USB供電一端)
TYPE-C USB數(shù)據(jù)線直接插到核心板的TYPE-C USB座子,另外一端連接電腦USB口;
準(zhǔn)備軟件環(huán)境:
手機打開WiFi熱點zhutianhua 123qweasd,修改netdrv_multiple.lua中的WiFi信息;
netdrv_device.lua打開多網(wǎng)卡驅(qū)動單獨演示;
Luatools燒錄內(nèi)核固件以及修改后的demo腳本;
多網(wǎng)卡演示:
開機后,在Luatools運行日志中搜索 new adapter,如下圖所示,按照紅色文字操作演示

單網(wǎng)卡演示:
直接運行并且分析日志;
六、如何分析LuatOS MQTT日志
在mqtt開發(fā)使用過程中,或多或少,我們都會遇到一些問題,遇到問題時,如果我們自己可以根據(jù)日志進行初步分析,可能會大大提高解決問題的效率;在本章節(jié)我們提供三種日志分析方法;
6.1 三種日志分析方法
6.1.1 Luatools抓取的應(yīng)用日志分析
這部分是Luatools抓取的應(yīng)用日志,在Luatools的主窗口可以實時顯示,如下圖所示:

應(yīng)用日志分為兩種類型:
一部分是Lua腳本中輸出的日志,有D/、I/、W/、E/幾種前綴,這部分日志,大家根據(jù)自己編寫的Lua腳本邏輯自行分析即可;
另一部分是內(nèi)核固件中輸出的一些關(guān)鍵日志,沒有D/、I/、W/、E/幾種前綴,這部分日志,大家不知道所表示的意義,我們會在本章節(jié)的以下內(nèi)容中針對一些常見的日志進行逐一分析;
6.1.2 抓取LuatOS模擬器上的網(wǎng)絡(luò)交互包進行分析
mqtt應(yīng)用完全可以在LuatOS模擬器上運行,所以我們可以使用LuatOS模擬器來運行自己的程序;
在運行過程中使用wireshark抓取網(wǎng)絡(luò)交互包,進行詳細(xì)分析,例如下圖是在LuatOS模擬器運行過程中,tls+單向認(rèn)證連接服務(wù)器失敗的網(wǎng)絡(luò)交互包,從交互包中可以準(zhǔn)確的得知,失敗原因是Unknown CA,未識別的CA證書

這種分析方法對于解決socket,http,mqtt,ftp等網(wǎng)絡(luò)應(yīng)用的問題,幫助很大!
6.1.3 Luatools抓取的底層日志網(wǎng)絡(luò)交互包分析
這里所說的抓取底層日志網(wǎng)絡(luò)交互包分析,和 9.1.2 抓取LuatOS模擬器上的網(wǎng)絡(luò)交互包進行分析 相比,一個是在真實的硬件板上(例如Air8000)運行抓日志,一個是在LuatOS模擬器上運行抓網(wǎng)絡(luò)交互包;
在真實的硬件板上抓到日志后,然后再使用底層日志分析工具提取出來網(wǎng)絡(luò)交互包,然后再使用wireshark對提取出來的網(wǎng)絡(luò)交互包進行分析;
和模擬器運行直接抓網(wǎng)絡(luò)交互包相比,這種方式有以下幾種明顯的缺點:
1、抓取日志以及分析操作繁瑣;
2、硬件板內(nèi)存資源有限,抓到的網(wǎng)絡(luò)日志會丟包,特別是網(wǎng)絡(luò)數(shù)據(jù)交互頻繁的時候,丟包問題嚴(yán)重;
雖然有以上兩項缺點,但是因為是真實的運行環(huán)境,對于LuatOS模擬器運行無法分析解決的問題,最終還是要通過這種方式解決;
如果需要用到這種方式來分析解決問題,當(dāng)前階段,只需要提交Luatools抓到的日志給技術(shù)人員即可,由技術(shù)人員進行分析;
后續(xù)我們也會寫一篇文章,單獨講解如何使用這種方法分析問題;大家如果有興趣,可自行查看文檔學(xué)習(xí)。
所以,在本章節(jié)接下來的內(nèi)容中,我會針對一些常見的問題,分別使用Luatools應(yīng)用日志分析和LuatOS模擬器上的網(wǎng)絡(luò)交互包分析這兩種方法來講述;
6.2 mqtt創(chuàng)建
6.2.1 創(chuàng)建失敗
Luatools應(yīng)用日志分析
使用mqtt.create(adapter, host, port, ssl, isipv6)接口創(chuàng)建mqtt對象時,常見的錯誤日志,如下圖所示:

這種異常日志的意思是:無法為mqtt對象分配資源;
出現(xiàn)這種異常,通常是因為同時存在的mqtt對象數(shù)量超過了內(nèi)核固件限制的最大數(shù)量:
Air780系列/Air8000系列的模組,允許同時存在的mqtt對象數(shù)量為64個;
Air6101系列/Air8101系列的模組,允許同時存在的mqtt對象數(shù)量為32個;
通常是以下兩種原因造成的:
1. 項目中開發(fā)業(yè)務(wù)代碼時,mqtt.create和mqtt.close是一對逆操作,如果不斷的create,但是沒有release,就會出現(xiàn)這種問題,這種錯誤的使用方式比較常見;
例如在一個實際項目中,創(chuàng)建mqtt,mqtt連接,mqtt收發(fā)業(yè)務(wù)邏輯處理,如果業(yè)務(wù)邏輯處理過程中出現(xiàn)異常,就會斷開mqtt,然后再銷毀mqtt;這是一套完整的操作,出現(xiàn)異常后,會從mqtt創(chuàng)建開始重試;在實際代碼開發(fā)過程中,有可能會忘記銷毀mqtt;這樣的話,每次重試都會創(chuàng)建一個新的mqtt;隨著重試的次數(shù)增多,最終同時存在的mqtt對象就會超過上限而出現(xiàn)錯誤;例如下圖中,如果漏寫了紅框內(nèi)的代碼,隨著時間的推移,最終就會出現(xiàn)問題

2. 項目中正常業(yè)務(wù)邏輯,同時使用的mqtt對象數(shù)量超過了限制,這種情況下,只能簡化業(yè)務(wù)邏輯,減少對mqtt、的使用;不過這種情況幾乎不會出現(xiàn),因為一個項目的正常業(yè)務(wù)邏輯,幾乎不會出現(xiàn)同時使用幾十個mqtt對象的情況;
LuatOS模擬器上的網(wǎng)絡(luò)交互包分析
mqttt創(chuàng)建失敗,不涉及網(wǎng)絡(luò)交互包,所以不適用于此方法進行分析;
6.3 mqtt連接
6.3.1 dns解析失敗
Luatools應(yīng)用日志分析
如下圖所示,解析域名:lbsmqtt.airm2m4289894.com;
一共四個DNS服務(wù)器,每個服務(wù)器嘗試解析3次,最終沒有解析成功,提示:dns_run 649:no ipv6, no ipv4

出現(xiàn)此種錯誤,可以通過以下幾步嘗試分析解決:
1、確認(rèn)下域名輸入是否正確;
2、參考socket.setDNS(adapter_id, dns_index, ip)的說明配置自定義的DNS服務(wù)器;
LuatOS模擬器上的網(wǎng)絡(luò)交互包分析

6.3.2 tcp握手連接失敗
Luatools應(yīng)用日志分析
當(dāng)對端ip地址存在,端口不存在時,例如:連接我們提供的mqtt測試服務(wù)器,端口38845(不存在)
會有以下異常日志:net_lwip_tcp_err_cb 662:adapter 1 socket 2 not closing, but error -13

這里有一個錯誤值:-13,表示:
ERR_ABRT = -13
含義:連接被中止。
場景:TCP 連接被本地或?qū)Χ水惓V兄梗ㄈ缡盏?RST 包,或本地主動調(diào)用tcp_abort)。
具體到本日志,因為對端ip不存在,所以應(yīng)該是tcp三次握手過程中,超時,內(nèi)核固件主動斷開了連接;
還有一種常見的錯誤是對端IP不存在,此時在異常日志中的錯誤值很可能是:-13;
如下圖所示,連接一個不存在IP地址:113.126.89.86
net_lwip_tcp_err_cb 662:adapter 1 socket 0 not closing, but error -13

這里有一個錯誤值:-13,表示:
ERR_ABRT = -13
含義:連接被中止。
場景:TCP 連接被本地或?qū)Χ水惓V兄梗ㄈ缡盏?RST 包,或本地主動調(diào)用tcp_abort)。
具體到本日志,因為對端ip不存在,所以應(yīng)該是tcp三次握手過程中,超時,內(nèi)核固件主動斷開了連接;
在LuatOS內(nèi)核固件中,使用的是LwIP協(xié)議棧,此處的錯誤值的完整的取值范圍如下所述(大家在平時開發(fā)過程中,如果遇到異常,根據(jù)日志中的錯誤值,可以參考這部分說明自行簡單分析下是什么原因):
1. ERR_OK = 0:無錯誤,操作成功;
2. ERR_MEM = -1:內(nèi)存分配失??;
3. ERR_BUF = -2:
含義:緩沖區(qū)錯誤(不足或大小不匹配)。
場景:發(fā)送數(shù)據(jù)時緩沖區(qū)空間不足(如pbuf大小不夠存放待發(fā)送數(shù)據(jù)),或接收時緩沖區(qū)溢出。
4. ERR_TIMEOUT = -3
含義:操作超時。
場景:TCP 連接超時(未收到 SYN-ACK)、ARP 請求超時(未收到目標(biāo) MAC 地址應(yīng)答)、netconn_recv等待數(shù)據(jù)超時等。
5. ERR_RTE = -4
含義:路由錯誤(無可用路由)。
場景:發(fā)送 IP 數(shù)據(jù)包時,lwip 路由表中找不到目標(biāo) IP 地址的有效路由(如未配置默認(rèn)網(wǎng)關(guān)且無直連路由)。
6. ERR_INPROGRESS = -5
含義:操作正在進行中。
場景:非阻塞模式下的操作(如tcp_connect)尚未完成,需等待后續(xù)回調(diào)通知結(jié)果。
7. ERR_VAL = -6
含義:無效值(參數(shù)值非法)。
場景:傳入 API 的參數(shù)值超出合法范圍(如端口號為 0 或大于 65535,IP 地址格式錯誤等)。
8. ERR_WOULDBLOCK = -7
含義:操作會阻塞(非阻塞模式下)。
場景:非阻塞模式下調(diào)用netconn_recv時暫無數(shù)據(jù)可接收,或tcp_send時發(fā)送窗口未滿導(dǎo)致無法立即發(fā)送。
9. ERR_USE = -8
含義:地址已被使用。
場景:綁定端口時(udp_bind、tcp_bind),指定的端口已被其他連接占用。
10. ERR_ALREADY = -9
含義:已在連接中。
場景:對已處于連接過程中的 TCP 控制塊再次調(diào)用tcp_connect。
11. ERR_ISCONN = -10
含義:連接已建立。
場景:對已連接的 TCP 連接再次調(diào)用tcp_connect,或?qū)σ堰B接的netconn執(zhí)行不需要連接的操作(如bind)。
12. ERR_CONN = -11
含義:未連接狀態(tài)。
場景:對未建立連接的netconn調(diào)用send,或關(guān)閉未連接的連接。
13. ERR_IF = -12
含義:底層網(wǎng)絡(luò)接口(netif)錯誤。
場景:網(wǎng)絡(luò)接口未初始化、鏈路斷開(如以太網(wǎng)物理層未連接),導(dǎo)致數(shù)據(jù)包無法發(fā)送。
14. ERR_ABRT = -13
含義:連接被中止。
場景:TCP 連接被本地或?qū)Χ水惓V兄梗ㄈ缡盏?RST 包,或本地主動調(diào)用tcp_abort)。
15. ERR_RST = -14
含義:連接被重置。
場景:TCP 連接收到對端發(fā)送的 RST(重置)報文,導(dǎo)致連接強制關(guān)閉。
16. ERR_CLSD = -15
含義:連接已關(guān)閉。
場景:對已正常關(guān)閉的連接執(zhí)行讀寫操作(如tcp_send在連接關(guān)閉后調(diào)用)。
17. ERR_ARG = -16
含義:非法參數(shù)(參數(shù)類型或指針無效)。
場景:傳入 API 的參數(shù)為NULL(如netconn_new傳入無效的協(xié)議類型,tcp_send傳入NULL緩沖區(qū))。
18. ERR_IF_HIGH_WATER = -17
含義:底層網(wǎng)絡(luò)接口達(dá)到高水位線(緩沖區(qū)滿)。
場景:網(wǎng)絡(luò)接口發(fā)送緩沖區(qū)已滿,暫時無法接收新的發(fā)送請求(通常用于流量控制)。
19. ERR_IF_SUSPEND = -18
含義:底層網(wǎng)絡(luò)接口被掛起。
場景:網(wǎng)絡(luò)接口因某種原因(如手動暫停、錯誤恢復(fù)中)暫時不可用。
20. ERR_IF_OOS = -19
含義:底層網(wǎng)絡(luò)接口處于 “OutOfService”(服務(wù)中斷)狀態(tài)。
場景:網(wǎng)絡(luò)接口硬件故障、驅(qū)動錯誤等導(dǎo)致完全無法提供服務(wù)。
LuatOS模擬器上的網(wǎng)絡(luò)交互包分析

6.3.3 tls握手連接失敗
Luatools應(yīng)用日志分析
在tls連接+僅支持單向認(rèn)證的場景中,如果我們在客戶端配置了錯誤的CA證書,本來應(yīng)該是openluat_root_ca.crt文件中的內(nèi)容,現(xiàn)在配置為了錯誤的baidu_parent_ca.crt文件,如下圖所示:

在連接過程中,會有以下異常日志: network_state_shakehand 807:0x2700, 3

這里有一個錯誤值:0x2700,MBEDTLS_ERR_X509_CERT_VERIFY_FAILED,表示證書驗證失??;
在LuatOS內(nèi)核固件中,使用的是MbedTLS開源庫,TLS握手連接過程中,MbedTLS中有以下常見的錯誤值(大家在平時開發(fā)過程中,如果遇到異常,根據(jù)日志中的錯誤值,可以參考這部分說明自行簡單分析下是什么原因,如果這里沒有覆蓋出現(xiàn)的錯誤值,可以使用AI工具提問,例如錯誤值為0x2700,可以提問:tls握手連接過程中,0x2700表示什么錯誤):
1、基礎(chǔ)加密 / 解密錯誤
MBEDTLS_ERR_SSL_DECRYPTION_FAILED(0x7080):解密失?。ㄈ鐚ΨQ加密密鑰錯誤、密文損壞)。
MBEDTLS_ERR_SSL_BAD_HMAC(0x7082):HMAC 驗證失敗(消息完整性校驗不通過,可能被篡改)。
MBEDTLS_ERR_SSL_BAD_RECORD_MAC(0x7084):記錄層 MAC 校驗失敗(與 HMAC 類似,針對 TLS 記錄的完整性)。
2、協(xié)議版本與協(xié)商錯誤
MBEDTLS_ERR_SSL_UNSUPPORTED_VERSION(0x7000):不支持對方的 TLS 版本(如客戶端要求 TLS 1.0,服務(wù)器僅支持 TLS 1.2+)。
MBEDTLS_ERR_SSL_VERSION_MISMATCH(0x7002):版本協(xié)商不匹配(如客戶端和服務(wù)器協(xié)商的版本不一致)。
3、密碼套件與算法錯誤
MBEDTLS_ERR_SSL_NO_SHARED_CIPHER(0x7004):無共同支持的密碼套件(客戶端與服務(wù)器的密碼套件列表無交集)。
MBEDTLS_ERR_SSL_UNSUPPORTED_CIPHERSUITE(0x7006):對方選擇的密碼套件本地不支持。
MBEDTLS_ERR_SSL_UNSUPPORTED_EXTENSION(0x7008):不支持對方發(fā)送的 TLS 擴展(如 ALPN、SNI 擴展不被認(rèn)可)。
4、證書驗證錯誤
MBEDTLS_ERR_X509_CERT_VERIFY_FAILED(0x2700):證書驗證失?。ㄈ绾灻麩o效、過期、吊銷)。
MBEDTLS_ERR_X509_UNKNOWN_CA(0x2702):證書鏈中存在未知 CA(根證書不被信任)。
MBEDTLS_ERR_SSL_CERTIFICATE_REQUIRED(0x7040):服務(wù)器要求客戶端證書,但客戶端未提供。
MBEDTLS_ERR_SSL_BAD_CERTIFICATE(0x7042):證書格式錯誤或內(nèi)容無效(如解析失敗、字段不合法)。
5、密鑰交換與認(rèn)證錯誤
MBEDTLS_ERR_SSL_KEY_EXCHANGE_FAILED(0x7020):密鑰交換過程失?。ㄈ?RSA 密鑰解密失敗、ECDH 密鑰協(xié)商錯誤)。
MBEDTLS_ERR_SSL_BAD_CLIENT_KEY_EXCHANGE(0x7022):客戶端密鑰交換消息格式錯誤或內(nèi)容無效。
MBEDTLS_ERR_SSL_BAD_CERTIFICATE_VERIFY(0x7044):客戶端證書驗證消息(CertificateVerify)無效(如簽名不匹配)。
6、握手流程與消息錯誤
MBEDTLS_ERR_SSL_UNEXPECTED_MESSAGE(0x7060):收到意外的握手消息(如流程順序錯誤,如先收到 Finished 再收到 Certificate)。
MBEDTLS_ERR_SSL_INVALID_HANDSHAKE_MESSAGE(0x7062):握手消息格式無效(如長度錯誤、字段缺失)。
MBEDTLS_ERR_SSL_HANDSHAKE_FAILURE(0x7064):握手失?。ㄍㄓ缅e誤,可能因上述多種原因?qū)е?,如服?wù)器拒絕客戶端配置)。
MBEDTLS_ERR_SSL_HANDSHAKE_TIMEOUT(0x7066):握手超時(未在規(guī)定時間內(nèi)收到對方響應(yīng))。
7、連接與狀態(tài)錯誤
MBEDTLS_ERR_SSL_CONN_EOF(0x70a0):握手過程中連接被關(guān)閉(對方發(fā)送了關(guān)閉通知)。
MBEDTLS_ERR_SSL_CONNECTION_RESET(0x70a2):連接被重置(如底層 TCP 連接斷開)。
MBEDTLS_ERR_SSL_WANT_READ / MBEDTLS_ERR_SSL_WANT_WRITE(0x70c0 / 0x70c2):非阻塞模式下需要繼續(xù)讀寫數(shù)據(jù)(非錯誤,需重試)。
LuatOS模擬器上的網(wǎng)絡(luò)交互包分析

6.3.4 mqtt connect失敗
Luatools應(yīng)用日志分析
tcp連接建立成功之后,mqtt client首先會發(fā)送一個MQTT CONNECT報文,如果mqtt broker服務(wù)器收到后,校驗CONNECT參數(shù)有誤,會返回一個MQTT CONNACK報文,報文中有錯誤碼;
如下圖所示,連接一個mqtts服務(wù)器,要求mqtt客戶端提供正確的用戶名和密碼,代碼中故意將用戶名和密碼配置錯誤:

在連接過程中,會有以下異常日志:CONACK 0x04

這里有一個錯誤碼:0x04,表示連接被拒絕(用戶名 / 密碼錯誤);
在LuatOS內(nèi)核固件中,使用的MQTT協(xié)議是3.1.1版本,
MQTT 3.1.1 定義了 6 種連接失敗的返回碼:

LuatOS模擬器上的網(wǎng)絡(luò)交互包分析
由于是MQTTS服務(wù)器,有加密安全限制,所以通過wireshark分析此問題,并不是很直觀,但是根據(jù)應(yīng)用報文的順序,也能大概分析出來是哪一步出錯了,如下圖所示,mqtt前兩條應(yīng)用報文分別是MQTT CONNECT和CONNACK,就可以初步判斷是CONNACK返回了失敗

6.4 mqtt收發(fā)數(shù)據(jù)和斷開
這些業(yè)務(wù)邏輯的異常情況,就不再一一分析了,大家遇到問題后,可以參考上面幾種異常的分析方法,自行分析;
今天的內(nèi)容就分享到這里了~
審核編輯 黃宇
-
物聯(lián)網(wǎng)
+關(guān)注
關(guān)注
2948文章
48072瀏覽量
417786 -
MQTT
+關(guān)注
關(guān)注
5文章
735瀏覽量
25242 -
LuatOS
+關(guān)注
關(guān)注
0文章
168瀏覽量
2740
發(fā)布評論請先 登錄
MQTT協(xié)議技術(shù)白皮書:構(gòu)建物聯(lián)網(wǎng)時代的輕量級通信基石
LuatOS:485 總線硬件設(shè)計要點與 exmodbus 庫開發(fā)實戰(zhàn)
零碳園區(qū)物聯(lián)網(wǎng)通信架構(gòu):多協(xié)議融合與網(wǎng)絡(luò)拓?fù)湓O(shè)計
MQTT協(xié)議為什么成為物聯(lián)網(wǎng)協(xié)議
北向MQTT工業(yè)物聯(lián)網(wǎng)網(wǎng)關(guān)是什么
明晚:睿擎物聯(lián)網(wǎng)實戰(zhàn):從傳感器采集到MQTT上云全流程解析|問學(xué)直播
MQTT網(wǎng)關(guān)對接到物聯(lián)網(wǎng)平臺快速開發(fā)應(yīng)用
創(chuàng)龍 瑞芯微 RK3588 國產(chǎn)2.4GHz八核 工業(yè)開發(fā)板—MQTT通信協(xié)議案例
Air780EPM嵌入式開發(fā):LuatOS下的MQTT通信實踐
LuatOS嵌入式開發(fā)實戰(zhàn):Air780EPM與MQTT通信
物聯(lián)網(wǎng)MQTT網(wǎng)關(guān)是什么
MQTT物聯(lián)網(wǎng)數(shù)據(jù)解析的難點有哪些?
Modbus RTU轉(zhuǎn)MQTT實現(xiàn)內(nèi)網(wǎng)通信的物聯(lián)網(wǎng)方案
基于LuatOS的MQTT物聯(lián)網(wǎng)通信全解
評論