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

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

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

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


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

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

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

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

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

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

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

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

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

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

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

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

4、心跳保活(客戶端 → Broker)
客戶端發送 PINGREQ 報文;
Broker 回復 PINGRESP 報文。

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

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

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

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

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

具體到LuatOS MQTT應用編程,我們僅需要關注上表中黃色背景的幾部分:
1、mqtt核心庫:這個庫是LuatOS MQTT應用編程的核心,在下一章節我們會重點講解;
2、socket核心庫、"IP_READY"和"IP_LOSE"消息:在LuatOS MQTT應用開發中,會主動查詢網卡的狀態,或者被動等待網卡狀態的變化通知,這部分功能就會用到socket核心庫、"IP_READY"和"IP_LOSE"消息;在本講最后我們學習LuatOS MQTT client應用開發框架時,再結合具體的代碼演示如何使用這部分功能;
3、exnetif擴展庫:在LuatOS MQTT應用開發中,會使用到單網卡(4G 、WiFi、以太網網卡中的一種)或者多網卡(4G 、WiFi、以太網網卡中的多種),exnetif擴展庫可以既簡捷又靈活的配置各種網卡,讓MQTT應用編程和網卡管理完全解耦;在本講最后我們學習LuatOS MQTT client應用開發框架時,我們結合具體的代碼來分析exnetif擴展庫如何使用;
四、LuatOS上的MQTT核心庫
LuatOS 提供 mqtt 核心庫,可以實現 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 秒后會開始自動重連;
上行數據均為一次性的,沒有緩存機制,更沒有上行的重試/重發機制;
4、重要的事情說 3 次:沒有重發機制,沒有重發機制,沒有重發機制
MQTT 協議中規定了重發機制,但那是云端/服務器端才會實現的機制,模塊端是沒有的;
上行失敗,唯一的可能性就是 TCP 鏈接出問題了,而 TCP 鏈接出問題的解決辦法就是重連;
模塊端不會保存任何上行數據,重連后也無法實現重發;
5、QoS僅支持0和1,不支持2;
4.1 常量詳解
核心庫常量,顧名思義是由 LuatOS 內核固件中定義的、不可重新賦值或修改的固定值,在腳本代碼中不需要聲明,可直接調用;

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 函數詳解
4.2.1 mqtt.create(adapter, host, port, ssl, isipv6)
功能
mqtt 客戶端創建;
注意事項
1. 返回值有返回 nil 的情況,在調用后必須進行檢查返回值是否為 nil,且要做邏輯處理;
參數
adapter

host

port

ssl

isipv6

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

示例

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

返回值
該接口無返回值,只需調用該接口執行相關操作,無需處理返回結果;
如果一定要把接口調用的結果賦值給一個變量,則這個變量就是一個nil值;
示例

4.2.3 mqttc:auth(client_id, username, password, cleanSession)
功能
配置 mqtt 三元組信息及會話清除策略(cleanSession);
注意事項
1. client_id、username、password 有長度限制,其中 client_id 和 username 限制不超過 192 字符,password 限制不超過 512 字符,超過限制會導致配置失敗;
2. 相同的 client_id 在同一服務器上會導致設備互相踢下線,需要確保 client_id 的唯一性;
參數
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)
功能
設置 mqtt 客戶端的心跳時間間隔,單位為秒(s);
注意事項
1. 參數 time 為可選值,默認值為 240 秒,最小值限制為 15 秒(若傳入小于 15 的值,會被強制設為 15 秒),最大值限制為 600 秒(若傳入大于 600 的值,會被強制設為 600 秒);
2. 實際發送心跳時間間隔 = 設置的心跳時間間隔 × 0.75,這是系統防止因網絡問題導致心跳包未在設定時間到達服務器時所執行的策略,從而確保了連接的穩定性;

3. 建議在 mqtt 連接前設置心跳參數,也可以在連接后動態調整心跳時間;
4. 網絡穩定環境可使用默認值 240 秒,或根據服務器要求調整;
5. 心跳間隔過短會增加功耗和網絡流量,過長可能導致連接被網絡斷開;
參數
time

返回值
該接口無返回值,只需調用該接口執行相關操作,無需處理返回結果;
如果一定要把接口調用的結果賦值給一個變量,則這個變量就是一個nil值;
示例

4.2.5 mqttc:will(topic, payload, qos, retain)
功能
設置 mqtt 客戶端的遺囑消息;
注意事項
1. 必須在調用 mqttc:connect() 之前調用此接口,否則遺囑消息設置不會生效;
參數
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 客戶端的自動重連功能,包括啟用/禁用自動重連以及設置重連周期;
注意事項
1. 該接口僅控制自動重連的配置,不直接執行重連操作;
2. 重連功能僅在連接斷開后生效,不會主動嘗試斷開現有連接;
3. 如果項目中使用多網卡功能,一定不要使用此接口,否則內核固件在自動重連時,可能會使用錯誤的網卡,導致無法上網;
參數
reconnect

reconnect_time

返回值
該接口無返回值,只需調用該接口執行相關操作,無需處理返回結果;
如果一定要把接口調用的結果賦值給一個變量,則這個變量就是一個nil值;
示例

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

返回值
該接口無返回值,只需調用該接口執行相關操作,無需處理返回結果;
如果一定要把接口調用的結果賦值給一個變量,則這個變量就是一個nil值;
示例

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

示例

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

示例

4.2.10 mqttc:subscribe(topic, qos)

功能
訂閱一個/多個主題(topic);
注意事項
1. 需要先建立 mqtt 客戶端且連接成功后再調用;
2. 訂閱時 topic 的命名要合法,qos 的值要在規定范圍內;
3. 多主題訂閱下,只要有任一主題訂閱失敗,整體返回 nil,需注意訂閱失敗的處理邏輯;
參數
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 主題發布消息;
注意事項
1. 調用此接口前,必須確保 mqtt 客戶端已經成功連接到服務器,否則發布操作可能失敗;
2. 遵循 mqtt 主題命名規范,避免使用特殊字符導致的兼容性問題;
參數
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,需注意處理;
參數
topic

返回值
該接口無返回值,只需調用該接口執行相關操作,無需處理返回結果;
如果一定要把接口調用的結果賦值給一個變量,則這個變量就是一個nil值;
示例

4.2.13 mqttc:disconnect()

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

示例

4.2.14 mqttc:close()

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

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

示例

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

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

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

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

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

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

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

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

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

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

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

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

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

這里有一個錯誤值:0x2700,MBEDTLS_ERR_X509_CERT_VERIFY_FAILED,表示證書驗證失敗;
在LuatOS內核固件中,使用的是MbedTLS開源庫,TLS握手連接過程中,MbedTLS中有以下常見的錯誤值(大家在平時開發過程中,如果遇到異常,根據日志中的錯誤值,可以參考這部分說明自行簡單分析下是什么原因,如果這里沒有覆蓋出現的錯誤值,可以使用AI工具提問,例如錯誤值為0x2700,可以提問:tls握手連接過程中,0x2700表示什么錯誤):
1、基礎加密 / 解密錯誤
MBEDTLS_ERR_SSL_DECRYPTION_FAILED(0x7080):解密失敗(如對稱加密密鑰錯誤、密文損壞)。
MBEDTLS_ERR_SSL_BAD_HMAC(0x7082):HMAC 驗證失敗(消息完整性校驗不通過,可能被篡改)。
MBEDTLS_ERR_SSL_BAD_RECORD_MAC(0x7084):記錄層 MAC 校驗失敗(與 HMAC 類似,針對 TLS 記錄的完整性)。
2、協議版本與協商錯誤
MBEDTLS_ERR_SSL_UNSUPPORTED_VERSION(0x7000):不支持對方的 TLS 版本(如客戶端要求 TLS 1.0,服務器僅支持 TLS 1.2+)。
MBEDTLS_ERR_SSL_VERSION_MISMATCH(0x7002):版本協商不匹配(如客戶端和服務器協商的版本不一致)。
3、密碼套件與算法錯誤
MBEDTLS_ERR_SSL_NO_SHARED_CIPHER(0x7004):無共同支持的密碼套件(客戶端與服務器的密碼套件列表無交集)。
MBEDTLS_ERR_SSL_UNSUPPORTED_CIPHERSUITE(0x7006):對方選擇的密碼套件本地不支持。
MBEDTLS_ERR_SSL_UNSUPPORTED_EXTENSION(0x7008):不支持對方發送的 TLS 擴展(如 ALPN、SNI 擴展不被認可)。
4、證書驗證錯誤
MBEDTLS_ERR_X509_CERT_VERIFY_FAILED(0x2700):證書驗證失敗(如簽名無效、過期、吊銷)。
MBEDTLS_ERR_X509_UNKNOWN_CA(0x2702):證書鏈中存在未知 CA(根證書不被信任)。
MBEDTLS_ERR_SSL_CERTIFICATE_REQUIRED(0x7040):服務器要求客戶端證書,但客戶端未提供。
MBEDTLS_ERR_SSL_BAD_CERTIFICATE(0x7042):證書格式錯誤或內容無效(如解析失敗、字段不合法)。
5、密鑰交換與認證錯誤
MBEDTLS_ERR_SSL_KEY_EXCHANGE_FAILED(0x7020):密鑰交換過程失敗(如 RSA 密鑰解密失敗、ECDH 密鑰協商錯誤)。
MBEDTLS_ERR_SSL_BAD_CLIENT_KEY_EXCHANGE(0x7022):客戶端密鑰交換消息格式錯誤或內容無效。
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):握手失敗(通用錯誤,可能因上述多種原因導致,如服務器拒絕客戶端配置)。
MBEDTLS_ERR_SSL_HANDSHAKE_TIMEOUT(0x7066):握手超時(未在規定時間內收到對方響應)。
7、連接與狀態錯誤
MBEDTLS_ERR_SSL_CONN_EOF(0x70a0):握手過程中連接被關閉(對方發送了關閉通知)。
MBEDTLS_ERR_SSL_CONNECTION_RESET(0x70a2):連接被重置(如底層 TCP 連接斷開)。
MBEDTLS_ERR_SSL_WANT_READ / MBEDTLS_ERR_SSL_WANT_WRITE(0x70c0 / 0x70c2):非阻塞模式下需要繼續讀寫數據(非錯誤,需重試)。
LuatOS模擬器上的網絡交互包分析

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

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

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

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

6.4 mqtt收發數據和斷開
這些業務邏輯的異常情況,就不再一一分析了,大家遇到問題后,可以參考上面幾種異常的分析方法,自行分析;
今天的內容就分享到這里了~
審核編輯 黃宇
-
物聯網
+關注
關注
2945文章
47818瀏覽量
414834 -
MQTT
+關注
關注
5文章
733瀏覽量
25070 -
LuatOS
+關注
關注
0文章
156瀏覽量
2695
發布評論請先 登錄
零碳園區物聯網通信架構:多協議融合與網絡拓撲設計
MQTT協議為什么成為物聯網協議
北向MQTT工業物聯網網關是什么
MQTT網關對接到物聯網平臺快速開發應用
創龍 瑞芯微 RK3588 國產2.4GHz八核 工業開發板—MQTT通信協議案例
MQTT物聯網數據解析的難點有哪些?
MQTT為何成為物聯網協議
KaihongOS操作系統:MQTT物聯網通訊協議
MQTT物聯網平臺有哪些?有哪些功能?
基于LuatOS的MQTT物聯網通信全解
評論