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

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

上面這張圖演示的是,應(yīng)用層以MQTT為例,TCP/IP數(shù)據(jù) 封裝/解封裝的過(guò)程,一個(gè)完整的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ù)向下傳,交給傳輸層,增加了傳輸層的頭部信息,在頭部信息中,有很多字段,其中有兩個(gè)字段分別是源端口號(hào)和目標(biāo)端口號(hào),這兩個(gè)端口號(hào)的作用是:定義了發(fā)送端和接收端的應(yīng)用程序,例如MQTT服務(wù)器,如果提供未加密的服務(wù),端口號(hào)一般為1883;如果提供加密的服務(wù),端口號(hào)一般為8883;通過(guò)端口號(hào),可以提供兩個(gè)主機(jī)之上端到端的通信;
3、傳輸層頭部+MQTT應(yīng)用數(shù)據(jù)向下傳,交給網(wǎng)絡(luò)層,增加了網(wǎng)絡(luò)層的頭部信息,在頭部信息中,有很多字段,其中有兩個(gè)字段分別是源IP地址和目標(biāo)IP地址,這兩個(gè)IP地址的作用是:定義了發(fā)送端和接收端的網(wǎng)絡(luò)設(shè)備地址;可以提供主機(jī)到主機(jī)的通信;
4、網(wǎng)絡(luò)層頭部+傳輸層頭部+MQTT應(yīng)用數(shù)據(jù)向下傳,交給數(shù)據(jù)鏈路層,增加了數(shù)據(jù)鏈路層的頭部信息;在這一層會(huì)根據(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ù)包,通過(guò)物理層轉(zhuǎn)換為0和1的電信號(hào),然后通過(guò)物理傳輸介質(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ā)送端;此時(shí)發(fā)送端和接收端的角色互換,再走一遍數(shù)據(jù)分層的封裝和解封裝過(guò)程;
接下來(lái)創(chuàng)建一個(gè)MQTT客戶端,去連接地址為"lbsmqtt.airm2m.com",端口為1884的MQTT服務(wù)器(這是一個(gè)不能商用的測(cè)試服務(wù)器),抓取了完整的MQTT CONNECT和MQTT CONNACK報(bào)文,我們來(lái)實(shí)際分析一下這兩種報(bào)文,加深一下對(duì)MQTT應(yīng)用的完整TCP/IP數(shù)據(jù)包的理解;
接下來(lái)我們打開這兩張圖片看一下:


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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

host

port

ssl

isipv6

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

示例

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

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

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

username

password

cleanSession

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

示例

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

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

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

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

payload

qos

retain

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

示例

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

reconnect_time

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

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

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

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

示例

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

示例

4.2.10 mqttc:subscribe(topic, qos)

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

qos

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

示例

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

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

data

qos

retain

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

示例

4.2.12 mqttc:unsubscribe(topic)

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

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

4.2.13 mqttc:disconnect()

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

示例

4.2.14 mqttc:close()

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

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

示例

五、LuatOS上的MQTT client 應(yīng)用開發(fā)框架
在理解了mqtt核心庫(kù)之后,我們?cè)賮?lái)實(shí)際看一個(gè)完整的mqtt client長(zhǎng)連接的demo項(xiàng)目代碼,重點(diǎn)分析下如何在項(xiàng)目中使用LuatOS mqttt核心庫(kù);
mqtt client長(zhǎng)連接的demo項(xiàng)目代碼路徑:Air8000 mqtt demo;
5.1 總體設(shè)計(jì)框圖
demo項(xiàng)目的總體設(shè)計(jì)框圖如下:

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

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

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

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

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

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

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

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

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

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

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

6.3.3 tls握手連接失敗
Luatools應(yīng)用日志分析
在tls連接+僅支持單向認(rèn)證的場(chǎng)景中,如果我們?cè)诳蛻舳伺渲昧隋e(cuò)誤的CA證書,本來(lái)應(yīng)該是openluat_root_ca.crt文件中的內(nèi)容,現(xiàn)在配置為了錯(cuò)誤的baidu_parent_ca.crt文件,如下圖所示:

在連接過(guò)程中,會(huì)有以下異常日志: network_state_shakehand 807:0x2700, 3

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

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

在連接過(guò)程中,會(huì)有以下異常日志:CONACK 0x04

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

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

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