iosswiftsocketsudpqudpsocket

Reliable way of getting data continuously from UDP connection in iOS Swift


I am trying to create a listener from a drone to my device and receive the data from the from the drone over the UDP connection. The problem is when I start listening to the port where the drone is sending data it only able to read the data once. After that I get some bunch of logs, and I don't know what they are.

I have never implemented a UDP socket in iOS.

My current code


import UIKit
import Network

class ViewController: UIViewController {

    private var listener: NWListener?

    override func viewDidLoad() {
        super.viewDidLoad()
        startUDPListener()
    }

    private func startUDPListener() {
        do {
            let port: NWEndpoint.Port = 14550
            listener = try NWListener(using: .udp, on: port)

            listener?.stateUpdateHandler = { state in
                switch state {
                case .ready:
                    print("UDP Listener ready on port 14550")
                case .failed(let error):
                    print(" Listener failed:", error)
                default:
                    break
                }
            }

            listener?.newConnectionHandler = { [weak self] connection in
                print(" New UDP connection")
                self?.receive(on: connection)
                connection.start(queue: .main)
            }

            listener?.start(queue: .main)

        } catch {
            print(" Failed to start UDP listener:", error)
        }
    }

    private func receive(on connection: NWConnection) {
        connection.receiveMessage { [weak self] data, context, isComplete, error in
            if let data = data, !data.isEmpty {
                print("Received \(data.count) bytes")

                // Example: raw bytes
                print(data as NSData)

                // TODO: Decode MAVLink / custom protocol here
            }

            if error == nil {
                self?.receive(on: connection)
            }
        }
    }

    deinit {
        listener?.cancel()
    }
}

I am getting all these logs but not the data:

UDP Listener ready on port 14550
New UDP connection
Received 40 bytes



nw_path_evaluator_create_flow_inner failed NECP_CLIENT_ACTION_ADD_FLOW (null) evaluator parameters: udp, definite, server, attribution: developer, reuse local address, context: Default Network Context (private), proc: 041A3A9F-1BE2-3236-A958-3B4930D1B4C2, local address: 0.0.0.0:14550
nw_path_evaluator_create_flow_inner NECP_CLIENT_ACTION_ADD_FLOW 464C637F-0893-4191-92AA-B82E76CB684E [17: File exists]
nw_endpoint_flow_setup_channel [C2 192.168.4.1:14550 initial channel-flow (satisfied (Path is satisfied), viable, interface: en0[802.11], ipv4, dns, uses wifi)] failed to request add nexus flow
nw_endpoint_flow_failed_with_error [C2 192.168.4.1:14550 initial channel-flow (satisfied (Path is satisfied), viable, interface: en0[802.11], ipv4, dns, uses wifi)] already failing, returning
nw_endpoint_handler_create_from_protocol_listener [C2 192.168.4.1:14550 failed channel-flow (satisfied (Path is satisfied), viable, interface: en0[802.11], ipv4, dns, uses wifi)] nw_endpoint_flow_pre_attach_protocols
nw_connection_create_from_protocol_on_nw_queue [C2] Failed to create connection from listener
nw_ip_channel_inbox_handle_new_flow nw_connection_create_from_protocol_on_nw_queue failed
{length = 40, bytes = 0xfd1c0000 3f01011e 0000f3cf 1600acb7 ... 38ba98dc 543b049a }
nw_path_evaluator_create_flow_inner failed NECP_CLIENT_ACTION_ADD_FLOW (null) evaluator parameters: udp, definite, server, attribution: developer, reuse local address, context: Default Network Context (private), proc: 041A3A9F-1BE2-3236-A958-3B4930D1B4C2, local address: 0.0.0.0:14550
nw_path_evaluator_create_flow_inner NECP_CLIENT_ACTION_ADD_FLOW 464C637F-0893-4191-92AA-B82E76CB684E [17: File exists]
nw_endpoint_flow_setup_channel [C3 192.168.4.1:14550 initial channel-flow (satisfied (Path is satisfied), viable, interface: en0[802.11], ipv4, dns, uses wifi)] failed to request add nexus flow
nw_endpoint_flow_failed_with_error [C3 192.168.4.1:14550 initial channel-flow (satisfied (Path is satisfied), viable, interface: en0[802.11], ipv4, dns, uses wifi)] already failing, returning
nw_endpoint_handler_create_from_protocol_listener [C3 192.168.4.1:14550 failed channel-flow (satisfied (Path is satisfied), viable, interface: en0[802.11], ipv4, dns, uses wifi)] nw_endpoint_flow_pre_attach_protocols
nw_connection_create_from_protocol_on_nw_queue [C3] Failed to create connection from listener
nw_ip_channel_inbox_handle_new_flow nw_connection_create_from_protocol_on_nw_queue failed
nw_path_evaluator_create_flow_inner failed NECP_CLIENT_ACTION_ADD_FLOW (null) evaluator parameters: udp, definite, server, attribution: developer, reuse local address, context: Default Network Context (private), proc: 041A3A9F-1BE2-3236-A958-3B4930D1B4C2, local address: 0.0.0.0:14550
nw_path_evaluator_create_flow_inner NECP_CLIENT_ACTION_ADD_FLOW 464C637F-0893-4191-92AA-B82E76CB684E [17: File exists]

Solution

  • Hello Every one I have done some research and found a reliable solution regarding this.

    As I learn more about UDP I realize in UDP the listning and sending of data can be implemented independently so, below code I have used for contineouslly listning for the data hope this will help out. If needed I can also provide the working code for sending data in UDP. but my question is not abut sending data so for this question I am not adding that.

    import Foundation
    
    final class UdpListenerManager {
    
        private var socketFD: Int32 = -1
        private var isListening = false
        private let queue = DispatchQueue(label: "udp.listener.queue", qos: .background)
        private var currentTelemetry = TelemetryData()
        private var receiveBuffer = Data()
        
        var onTelemetryUpdate: ((TelemetryData) -> Void)?
        var onDataReceived: ((Data, sockaddr_in) -> Void)?
    
        func startListening(on port: UInt16) {
            guard !isListening else { return }
            isListening = true
    
            queue.async { [weak self] in
                self?.setupSocketAndListen(port: port)
            }
        }
    
        func stopListening() {
            isListening = false
    
            if socketFD >= 0 {
                close(socketFD)
                socketFD = -1
            }
        }
    
        deinit {
            stopListening()
        }
    
        private func setupSocketAndListen(port: UInt16) {
            socketFD = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)
            guard socketFD >= 0 else {
                print("Failed to create UDP socket")
                return
            }
    
            var reuse: Int32 = 1
            setsockopt(
                socketFD,
                SOL_SOCKET,
                SO_REUSEADDR,
                &reuse,
                socklen_t(MemoryLayout.size(ofValue: reuse))
            )
    
            var addr = sockaddr_in()
            addr.sin_family = sa_family_t(AF_INET)
            addr.sin_port = port.bigEndian
            addr.sin_addr = in_addr(s_addr: INADDR_ANY)
    
            let bindResult = withUnsafePointer(to: &addr) {
                $0.withMemoryRebound(to: sockaddr.self, capacity: 1) {
                    bind(socketFD, $0, socklen_t(MemoryLayout<sockaddr_in>.size))
                }
            }
    
            guard bindResult == 0 else {
                print("Bind failed on port \(port)")
                close(socketFD)
                socketFD = -1
                return
            }
    
            print("UDP listening on port \(port)")
            receiveLoop()
        }
    
        private func receiveLoop() {
            while isListening {
                var buffer = [UInt8](repeating: 0, count: 2048)
                var sourceAddr = sockaddr_in()
                var addrLen: socklen_t = socklen_t(MemoryLayout<sockaddr_in>.size)
    
                let receivedBytes = withUnsafeMutablePointer(to: &sourceAddr) {
                    $0.withMemoryRebound(to: sockaddr.self, capacity: 1) {
                        recvfrom(
                            socketFD,
                            &buffer,
                            buffer.count,
                            0,
                            $0,
                            &addrLen
                        )
                    }
                }
    
                guard receivedBytes > 0 else {
                    if isListening {
                        print("⚠️ recvfrom failed")
                    }
                    continue
                }
    
                let data = Data(buffer.prefix(receivedBytes))
                
                // Original raw callback
                onDataReceived?(data, sourceAddr)
                
                // Logic to decode and return telemetry data
                decodeMavlink(data)
            }
        }
    
        private func decodeMavlink(_ data: Data) {
            //do the decoding of the data according to your need
        }
    
        private func bytesToFloat(_ b: [UInt8], _ o: Int) -> Float {
            let u = UInt32(b[o]) | (UInt32(b[o+1]) << 8) | (UInt32(b[o+2]) << 16) | (UInt32(b[o+3]) << 24)
            return Float(bitPattern: u)
        }
    }