CocoaMQTT v2.0:首個支持 MQTT 5.0 的 iOS 客戶端

2017 年 8 月,OASIS MQTT Technical Committee 正式發佈了用於 Public Review 的 MQTT 5.0 的草案。2018 年,MQTT 5.0 正式發佈,然而直到目前蘋果生態裏仍沒有完整支持 MQTT 5.0 的客戶端 SDK 。

CocoaMQTT 是 EMQ 團隊爲 iOS 開發者提供的 MQTT 客戶端 SDK,目前在 iOS 開發者中有着較爲廣泛的使用。

爲彌補蘋果生態中對 MQTT 5.0 支持方面的空白,EMQ 團隊於近日正式發佈了 CocoaMQTT 全新版本 v2.0。CocoaMQTT v2.0 支持 MQTT 5.0,同時兼容 3.1.1 版本,支持 iOS、tvOS 與 OSX 操作系統。用戶現已可通過 CocoaMQTT 實現 iOS 系統設備連接 MQTT 5.0 雲服務,享受 MQTT 5.0 帶來的強大功能加成。

CocoaMQTT 使用 Swift 語言開發,而非 Objective-C。這是因爲 Swift 是一門類型安全的語言,對協議的支持更加豐富,配合擴展(extension)、泛型、關聯類型等可以實現面向協議編程,從而大大提高代碼的靈活性。

此外,在 WWDC 2021 大會上,蘋果宣佈了 Swift 語言中併發模型的重大更新:通過編譯器實現的 Actor 併發模型,新增 Actor 引用類型幫助開發者避免數據競爭問題。

因此,我們相信在 MQTT 相關的 I/O 密集型併發應用中,相較於 Objective-C,Swift 的表現將更值得期待。

MQTT 5.0 vs MQTT 3.1.1

MQTT 3.1.1 仍然有很多不完善的地方,例如連接異常斷開時無法通知原因到對端。MQTT 5.0 在 MQTT 3.1.1 的基礎上做了很多改變,但並不是向下兼容的。

MQTT 協議 5.0 版本新增了會話/消息延時功能、原因碼、主題別名、in-flight 流控、用戶屬性、共享訂閱等功能,以及用於增強認證安全的 AUTH 報文。其中,原因碼和用戶屬性 Property字段使得 MQTT 5.0 能夠攜帶更多上下文信息,從而解決在 3.1.1 版本因協議不完整而較難處理的問題。

MQTT 5.0 的主要功能優勢有:

  • 進一步支持更大規模的可擴展系統
  • 更加詳細的錯誤報告和處理機制
  • 容量探索和請求響應等通用模式的規範化操作
  • 可擴展的用戶屬性 (User Property)
  • 改進性能並支持小型客戶端
  • 會話保持和消息超時設置
  • 新增支持 Req/Rsp 消息模式

CocoaMQTT 客戶端的使用

本文將使用 EMQ 提供的 免費公共 MQTT 服務器 進行 CocoaMQTT 功能使用介紹,該服務基於 MQTT 雲服務 EMQ X Cloud 創建。

服務器接入信息如下:

  • Broker: broker-cn.emqx.io
  • TCP Port: 1883
  • Websocket Port: 8083
  • TCP/TLS 端口: 8883
  • Websocket/TLS 端口:8084

連接 MQTT 服務

我們看到 MQTT 5.0 增加了許多屬性,其中 Property 字段可以讓使用者根據自己的情況,更加細緻化去完成需求。

///MQTT 5.0
let clientID = "CocoaMQTT-" + String(ProcessInfo().processIdentifier)
let mqtt5 = CocoaMQTT5(clientID: clientID, host: "broker-cn.emqx.io", port: 1883)

let connectProperties = MqttConnectProperties()
connectProperties.topicAliasMaximum = 0
connectProperties.sessionExpiryInterval = 0
connectProperties.receiveMaximum = 100
connectProperties.maximumPacketSize = 500
mqtt5.connectProperties = connectProperties

mqtt5.username = "test"
mqtt5.password = "public"
mqtt5.willMessage = CocoaMQTTWill(topic: "/will", message: "dieout")
mqtt5.keepAlive = 60
mqtt5.delegate = self
mqtt5.connect()

///MQTT 3.1.1
let clientID = "CocoaMQTT-" + String(ProcessInfo().processIdentifier)
let mqtt = CocoaMQTT(clientID: clientID, host: "broker-cn.emqx.io", port: 1883)
mqtt.username = "test"
mqtt.password = "public"
mqtt.willMessage = CocoaMQTTWill(topic: "/will", message: "dieout")
mqtt.keepAlive = 60
mqtt.delegate = self
mqtt.connect()

訂閱主題

MQTT 5.0 在 MQTT 3.1.1 上面多了訂閱選項等操作。

///MQTT 5.0
mqtt5.subscribe("chat/room/animals/client/+", qos: CocoaMQTTQoS.qos1)
//or
//let subscriptions : [MqttSubscription] = [MqttSubscription(topic: "chat/room/animals/client/+"),MqttSubscription(topic: "chat/room/foods/client/+"),MqttSubscription(topic: "chat/room/trees/client/+")]
//mqtt.subscribe(subscriptions)

///MQTT 3.1.1
mqtt.subscribe("chat/room/animals/client/+", qos: CocoaMQTTQoS.qos1)
//or
//let subscriptions : [(String, CocoaMQTTQoS)] = [("chat/room/animals/client/+", qos: CocoaMQTTQoS.qos1),("chat/room/foods/client/+", qos: CocoaMQTTQoS.qos1),("chat/room/trees/client/+", qos: CocoaMQTTQoS.qos1)]
//mqtt.subscribe(subscriptions)

發佈消息

///MQTT 5.0
mqtt5!.publish("chat/room/animals/client/" + animal!, withString: message!, qos: .qos1, DUP: false, retained: false, properties: publishProperties)

///MQTT 3.1.1
mqtt!.publish("chat/room/animals/client/" + animal!, withString: message!, qos: .qos1)

自動重連

MQTT 是基於 TCP 長連接的協議,在實際使用的場景中,由於網絡故障或信號問題導致連接中斷是經常出現的問題。許多開發者會希望 SDK 能夠提供方便的自動重連方式。

///MQTT 5.0
mqtt5!.autoReconnect = true

///MQTT 3.1.1
mqtt!.autoReconnect = true

單雙向 SSL 連接

///MQTT 5.0
mqtt5!.enableSSL = true

///MQTT 3.1.1
mqtt!.enableSSL = true

let clientCertArray = getClientCertFromP12File(certName: "client-keycert", certPassword: "MySecretPassword")
var sslSettings: [String: NSObject] = [:]
sslSettings[kCFStreamSSLCertificates as String] = clientCertArray

///MQTT 5.0
mqtt5!.sslSettings = sslSettings

///MQTT 3.1.1
mqtt!.sslSettings = sslSettings

如果需要 .p12 文件可以在終端使用以下語句生成。

1openssl pkcs12 -export -clcerts -in client-cert.pem -inkey client-key.pem -out client.p12

設置保留消息和遺囑消息

MQTT 5.0 與 MQTT 3.1.1 比,多了更多的屬性設置可供用戶使用。

///MQTT 5.0
let lastWillMessage = CocoaMQTTMessage(topic: "/chat/room/animals/client/Sheep", string: "dieout")
lastWillMessage.retained = true
lastWillMessage.qos = .qos1
mqtt5!.willMessage = lastWillMessage

///MQTT 3.1.1
mqtt!.willMessage = CocoaMQTTMessage(topic: "/will", string: "dieout")

AUTH 報文

MQTT 單純通過 CONNECT 可能無法提供足夠的信息給 Server 進行身份認證,所以 MQTT 5.0 新增此功能。用於客戶端和服務器之間的加強認證。

  let authProperties = MqttAuthProperties()
        mqtt5!.auth(reasonCode: CocoaMQTTAUTHReasonCode.continueAuthentication, authProperties: authProperties)

iOS 應用後臺運行

推薦使用 「Background fetch」模式或 IOS 13 新增的「Background processing」模式。

Background Modes

如果使用 beginBackgroundTaskWithNameendBackgroundTask 相關的 API,可以保持 APP 在後臺運行 30 秒。

總結

至此,我們完成了使用 CocoaMQTT 客戶端連接到公共 MQTT 服務器,並實現了客戶端與 MQTT 服務器的連接、消息發佈和訂閱。

項目完整代碼請見 https://github.com/emqx/CocoaMQTT/tree/master/Example。

EMQ 致力於幫助用戶輕鬆便捷地使用 MQTT 開展物聯網業務,我們的一系列客戶端 SDK 均在持續開發中,敬請關注。

版權聲明: 本文爲 EMQ 原創,轉載請註明出處。

原文鏈接:https://www.emqx.com/zh/blog/ios-mqtt5-client

技術支持:如對本文或 EMQ 相關產品有疑問,可訪問 EMQ 問答社區 https://askemq.com 提問,我們將會及時回覆支持。

更多技術乾貨,歡迎關注我們公衆號【EMQ 中文社區】。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章