CocoaMQTT -MQTT Keep Alive

Chun-Li 春麗
6 min readDec 5, 2023

目錄
1. MQTT
2. CocoaMQTT Keep Alive
2-1 MQTT3.1.1
2-2 MQTT5
2-3 LWT

⦿ MQTT
⦿ CocoaMQTT Keep Alive
⦿ MQTT3.1.1
⦿ MQTT5
⦿ LWT

MQTT

在物聯網中,應用層協議我們常使用 MQTT,MQTT 相較於 HTTP 建立連線、斷線重連機制,是一個輕量化的協議,我們當然可以隨時 Call HTTP API,不過這一來一回(Request、Response)的速度等開銷就不若 MQTT 來的有利。

在 MQTT 裡,我們使用的是訂閱發佈(Subscribe、Publish)的方式,來讓設備端(Client)、控制端(APP or Broker),或是邊緣端(Edge)能夠隨著接收到的訊息作動。

當然,在物聯網中還有許多其他的協議值得研究,比方 Thread、Zigbee⋯⋯等,暫不在此篇文章的討論當中,但我們必須要知道有些協議其實是跨 OSI 的,就好比現在上網的人們是使用 TCP/IP 這個 IPS(Internet Protocol Suite),我們不會說上網是使用 TCP 連接,而排除 IP Address,甚至忽略 MAC Address。

OSI 是為我們刻繪出那看不見的東西,幫助理解進入連接、網路世界的核心概念,不過肉眼真實可見的大概就是物理層了,使用以太網路線,你可以看到 RJ45 的接頭,剝開網路線,可以看到四對銅絞線,打開 MCU,你可以看到 Tx、Rx 那 UART 的焊點,這些東西在我的文章中或可發現其蹤跡。

倘若要解析序列埠傳來的訊號,沒有示波器顯示的圖形,將會很難想像 Data 是什麼,又或者你在程式碼中看到 0x51,或對二進位的 Data 沒有感覺這是很正常的,不過物理層之外的其他層,函式庫也都幫你解決了。

在 ESP32 中,我們可以使用 Arduino 的套件 PubSubClient 以 MQTT 去連接 AWS,而在前一篇文章中,也告訴大家 Keep-Alive 的作動機制,如果 MQTT 在斷線時會採用什麼方式處理,接下來我們就在 iOS 中來看看怎麼做到 Keep-Alive 的吧!

回目錄

繼續閱讀|回目錄

CocoaMQTT Keep Alive

重點回顧,在執行 MQTT 連接後,比方說 DeviceBroker 之間如果持續沒有其他動作,那我們如何能確定他們仍保持連接呢?

這即是 Keep-Alive 的作用。

起始線為虛線,若 Keep-Alive 設定為 30 秒,而每一分鐘 Client 會固定傳送 Data 到 Broker,那麼在第一個 30 秒,Client 會發送 PINGREQ 給 Broker,Broker 也會傳送 PINGRESP 給 Client, 如此確認連接。

再 30 秒後 Data 開始傳送,既然有 Payload 就不用再送 PING,直到下一個 30 秒。

MQTT3.1.1

我們使用 CocoaMQTT 這個套件,你可以用 pod 來管理 dependency,或其他方式,接著,以 default host 為 broker-cn.emqx.io 這個 broker 來舉例,在 CocoaMQTT 中會看到如下:

CocoaMQTT(debug): ping
CocoaMQTT(debug): SEND: PING
CocoaMQTT(debug): =========================MQTT 3.1.1=========================
CocoaMQTT(debug): packetFixedHeaderType 192
CocoaMQTT(debug): remainingLen(len: len) [0]
CocoaMQTT(debug): variableHeader []
CocoaMQTT(debug): payload []
CocoaMQTT(debug): =============================================================
[TRACE] [mqttDidPing(_]:
CocoaMQTT(debug): socket wrote data -192
CocoaMQTT(debug): RECV: PONG
[TRACE] [mqttDidReceivePong(_]:

這意思是說,雙方有段時間沒聯繫了,由我方發動 ping(即 PINGREQ),在 MQTT 3.1.1 下,header type 是 192。

/// MQTT Frame Type
enum FrameType: UInt8 {
...
case pingreq = 0xC0
case pingresp = 0xD0
...
}

去找到 CocoaMQTT 的 FrameType 會發現 pingreq 是 0xC0,經過 16 進制(Hex)與 10 進制(Decimal)的轉換就是 192

既然只要 Ping Request,所以 payload 與 variableHeader 自然是空的,那麼,加起來的長度(remainingLen)也就會是 0。

並且看到這期間調用的 func 有 mqttDidPingmqttDidReceivePong,即是用來追蹤我方送 PING,以及收到 PONG(回應)是什麼時候。

MQTT5

同樣地,在使用 MQTT5 時,也可以追蹤 Keep-Alive 機制如何作動,若只取呼叫 function 前後,會出現如下:

[TRACE] [mqtt5DidPing(_]: 
[TRACE] [mqtt5DidReceivePong(_]:
[TRACE] [mqtt5DidPing(_]:
[TRACE] [mqtt5DidReceivePong(_]:
[TRACE] [mqtt5DidPing(_]:
[TRACE] [mqtt5DidReceivePong(_]:

如果 mqtt!.keepAlive = 60mqtt5!.keepAlive = 60,這表示一分鐘內 client 若沒有 payload 傳送到 Server,就會發送空的 payload 到 Server 來做連接確認。

LWT

同樣在 CocoaMQTT,我們可以設置 Last Will and Testament,遺囑訊息,以 MQTT5 舉例如下:

let lastWillMessage = CocoaMQTT5Message(topic: "/will", string: "dieout")
lastWillMessage.contentType = "JSON"
lastWillMessage.willResponseTopic = "/will"
lastWillMessage.willExpiryInterval = .max
lastWillMessage.willDelayInterval = 0
lastWillMessage.qos = .qos1

這個例子中可以看到這個遺囑訊息是 JSON 格式,在斷線會發出 dieout 這個 String,並且發在 /will 這個 topic,並以 QoS 1(至少一次)來發送。

這次就分享到這,感謝您的閱讀。

回目錄

繼續閱讀|回目錄

--

--

Chun-Li 春麗

Do not go gentle into that good night, Old age should burn and rave at close of day; Rage, rage, against the dying of the light.