Соединение MQTT через WebSocket и SSL не работает в iOS Swift

На данный момент у нас есть 2 приложения для iOS: одно разработано с помощью React-Native, а другое — с помощью Swift. Теперь проблема, с которой я сталкиваюсь, заключается в том, что всякий раз, когда я пытаюсь подключиться к Cloud-MQTT через порт WebSocket/SSL с использованием мобильных данных, он автоматически отключается iOSSwift APP(Если устройство iPhone и устройство Raspberry подключены к одной и той же сети, то все работает нормально. ), приложение React-Native работает нормально во всех сценариях.

А если я использую Plain MQTT Port, то соединение с мобильными данными работает нормально. Но проблема в том, что устройство Raspberry предоставляет учетные данные для подключения во время входа в систему. А шлюз предоставляет только порт WebSocket и SSL устройства. И все устройства будут иметь разные учетные данные, поэтому применение статического значения для порта не будет работать.

  • MQTT-брокер и порт
MQTT Cloud Broker
Server      driver.cloudmqtt.com
User :      xxxxxx
Password:   xxxxxx
Port:       18658 //Only this port is working
SSL Port:       28658
Websockets Port (TLS only):   38658
Mosquitto version:     1.5.7 
  • Код подключения
 func connect(host: String, port: Int, username: String?, password: String?, cleanSession: Bool) {
        
        guard !host.isEmpty else {
            delegate?.onMqttDisconnected()
            return
        }
        
        status = .connecting
        let clientId = "CONNECTED_GEAR-" + String(ProcessInfo().processIdentifier)
        
        if NetworkMonitor.shared.connection == .wifi {
            mqttClient = CocoaMQTT(clientID: clientId, host: host, port: UInt16(port))
            if let mqttClient = mqttClient {
                UserManager.shared.isConnected = true
                mqttClient.username = username
                mqttClient.password = password
                mqttClient.keepAlive = UInt16(60)
                mqttClient.cleanSession = cleanSession
                mqttClient.backgroundOnSocket = true
                mqttClient.delegate = self
               
               _ = mqttClient.connect()
            } else {
                
                UserManager.shared.isConnected = false
                delegate?.onMqttError(message: "Mqtt initialization error")
                status = .error
            }
        } else {
            
                 let socket = CocoaMQTTWebSocket()
               
                 socket.enableSSL = false
        
                mqttClient = CocoaMQTT(clientID: clientId, host: host, port: UInt16(port), socket: socket)
                if let mqttClient = mqttClient {
                    UserManager.shared.isConnected = true
                    mqttClient.username = username
                    mqttClient.password = password
                    mqttClient.keepAlive = UInt16(60)
                    mqttClient.cleanSession = cleanSession
                    

                    mqttClient.logLevel = .debug
                    mqttClient.autoReconnect = false
                    mqttClient.allowUntrustCACertificate = true
                    mqttClient.backgroundOnSocket = true
                    mqttClient.delegate = self

                    _ = mqttClient.connect()
                } else {
                    
                    UserManager.shared.isConnected = false
                    delegate?.onMqttError(message: "Mqtt initialization error")
                    status = .error
                }
        }
       
    }
  • Журналы MQTT

Когда я установил EnableSSL = false

Network reachable via Cellular 
External Host: driver.cloudmqtt.com
External Port: 38658
External User: xxxxxxx
External Password: xxxxxxx
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): =============================================================
CocoaMQTT(info): Connected to 3.83.156.245 : 38658
CocoaMQTT(info): Enable backgrounding socket successfully
CocoaMQTT(debug): SEND: CONNECT(id: CONNECTED_GEAR-933, username: xxxxxxx, password: xxxxxxx, keepAlive : 60, cleansess: false)
CocoaMQTT(debug): =========================MQTT 3.1.1=========================
CocoaMQTT(debug): packetFixedHeaderType 16
CocoaMQTT(debug): remainingLen(len: len) [54]
CocoaMQTT(debug): variableHeader [0, 4, 77, 81, 84, 84, 4, 192, 0, 60]
CocoaMQTT(debug): payload [0, 18, 67, 79, 78, 78, 69, 67, 84, 69, 68, 95, 71, 69, 65, 82, 45, 57, 51, 51, 0, 8, 104, 101, 121, 103, 108, 105, 104, 103, 0, 12, 71, 103, 107, 106, 74, 101, 110, 81, 114, 119, 120, 48]
CocoaMQTT(debug): =============================================================
CocoaMQTT(debug): socket wrote data -192
CocoaMQTT(debug): socket wrote data 0
CocoaMQTT(debug): socket disconnected
Disconnect: Socket closed by remote peer

Когда я установил EnableSSL = true

External Host: driver.cloudmqtt.com
External Port: 38658
External User: xxxxxxx
External Password: xxxxxxx
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): =============================================================
CocoaMQTT(info): Connected to 3.83.156.245 : 38658
CocoaMQTT(info): Enable backgrounding socket successfully
CocoaMQTT(debug): socket wrote data -192
CocoaMQTT(debug): socket disconnected
Disconnect: The operation couldn’t be completed. (kCFStreamErrorDomainSSL error -9806.)
CocoaMQTT(info): Try reconnect to server after 1s

CocoaMQTT(info): Connected to 3.83.156.245 : 38658
CocoaMQTT(info): Enable backgrounding socket successfully
CocoaMQTT(debug): Call the SSL/TLS manually validating function
  • Я прочитал так много вопросов, блогов и документов, касающихся порта MQTT Over WebSocket and SSL, но ничего не работает.

Примечание: - Я установил AllowArbitraryLoads = true в info.plist.

К вашему сведению: из-за проблемы с GeoFence в React-Native мы разработали приложения на Native Swift и Kotlin.

Вы искали ошибку kCFStreamErrorDomainSSL error -9806.?

hardillb 08.07.2024 15:00

Да, это причина добавить AllowArbitraryLoads в info.plist

steveSarsawa 08.07.2024 15:03
Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
0
2
52
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Наконец я нашел решение, и теперь MQTT Connection и WebSocket Connection в сети любого типа работают нормально.

 func connect(host: String, port: Int, username: String?, password: String?, cleanSession: Bool) {
       
        guard !host.isEmpty else {
            delegate?.onMqttDisconnected()
            return
        }
        
        status = .connecting
        let clientId = "CONNECTED_GEAR-" + String(ProcessInfo().processIdentifier)
        

        //During connection without HomeNetwork wifi, required to connect with WebSocket. That's the main reason to implement different connection request for Network type.
        if NetworkMonitor.shared.connection != .wifi {
           
            
            let websocket = CocoaMQTTWebSocket(uri: "/ws")
            
            mqttClient = CocoaMQTT(clientID: clientId, host: host, port: UInt16(port), socket: websocket)
            
            if let mqttClient = mqttClient {
                
                UserManager.shared.isConnected = true
                mqttClient.enableSSL = true
                mqttClient.username = username
                mqttClient.password = password
                mqttClient.keepAlive = UInt16(60)
                mqttClient.cleanSession = cleanSession
                mqttClient.backgroundOnSocket = true
                mqttClient.allowUntrustCACertificate = true
                
                mqttClient.willMessage = CocoaMQTTMessage(topic: "/will", string: "dieout")
                mqttClient.autoReconnect = true
                mqttClient.logLevel = .debug
                
                mqttClient.delegate = self
                _ = mqttClient.connect()
            } else {
                
                UserManager.shared.isConnected = false
                delegate?.onMqttError(message: "Mqtt initialization error")
                status = .error
            }
            
        } else {
            
            mqttClient = CocoaMQTT(clientID: clientId, host: host, port: UInt16(port))
            
            if let mqttClient = mqttClient {
                UserManager.shared.isConnected = true
                mqttClient.delegate = self
                mqttClient.username = username
                mqttClient.password = password
                mqttClient.keepAlive = UInt16(60)
                mqttClient.cleanSession = cleanSession
                mqttClient.backgroundOnSocket = true
                mqttClient.logLevel = .error
                mqttClient.deliverTimeout = 15.00
                mqttClient.autoReconnect = true
                mqttClient.willMessage = CocoaMQTTMessage(topic: "/will", string: "dieout")
               _ = mqttClient.connect()
            } else {
                
                UserManager.shared.isConnected = false
                delegate?.onMqttError(message: "Mqtt initialization error")
                status = .error
            }
        }
        
       
    }
  • Ниже метод delegate отсутствовал

Примечание. – Я запросил чатGPT и получил предложение добавить эти 2 метода.

extension MqttManager: CocoaMQTTDelegate {
    
    func mqtt(_ mqtt: CocoaMQTT, didReceive trust: SecTrust, completionHandler: @escaping (Bool) -> Void) {
            // Implement your custom SSL validation logic here.
            // For example, you might want to always trust the certificate for testing purposes:
            completionHandler(true)
        }

        func mqtt(_ mqtt: CocoaMQTT, didReceive challenge: URLAuthenticationChallenge, completionHandler: @escaping (URLSession.AuthChallengeDisposition, URLCredential?) -> Void) {
            if challenge.protectionSpace.authenticationMethod == NSURLAuthenticationMethodServerTrust {
                if let serverTrust = challenge.protectionSpace.serverTrust {
                    completionHandler(.useCredential, URLCredential(trust: serverTrust))
                    return
                }
            }
            completionHandler(.performDefaultHandling, nil)
        }
    
    func mqttUrlSession(_ mqtt: CocoaMQTT, didReceiveTrust trust: SecTrust, didReceiveChallenge challenge: URLAuthenticationChallenge, completionHandler: @escaping (URLSession.AuthChallengeDisposition, URLCredential?) -> Void) {
        
        print("\(#function), \n result:- \(challenge.debugDescription)")
    }
    
  • info.plist
<key>NSAppTransportSecurity</key>
    <dict>
        <key>NSExceptionDomains</key>
        <dict>
            <key>my-domain.cloudmqtt.com</key>
            <dict>
                <key>NSAllowsArbitraryLoads</key>
                <true/>
                <key>NSExceptionAllowsInsecureHTTPLoads</key>
                <true/>
                <key>NSIncludesSubdomains</key>
                <true/>
                <key>NSTemporaryExceptionRequiresForwardSecrecy</key>
                <false/>
            </dict>
        </dict>
    </dict>

Другие вопросы по теме