UDP listener only receives one packet and show Errors

I have an issue where my iPhone app needs to listen for a UDP packet and print that data to the screen. UDP packets are sent by a PC connecting to a Wifi router and my Iphone is also connecting to it and listening on the correct port. When running the App, it works at the first moment and receives the first UDP packet co, but after receiving the first packet, it stops receiving more and I see errors in the logs. These errors only appear when I test directly on the Iphone receiving packets through the router's wifi network with ip: 192.168.0.1. When I run through the simulator using localhost (127.0.0.1 ) the errors do not appear and I receive all packages correctly. Code for UDP listener:

class UDPConecct: ObservableObject {
        
        var listener: NWListener?
        var connection: NWConnection?
        var queue = DispatchQueue.global(qos: .userInitiated)
        /// New data will be place in this variable to be received by observers
        @Published private(set) public var messageReceived: Data?
        /// When there is an active listening NWConnection this will be `true`
        @Published private(set) public var isReady: Bool = false
        /// Default value `true`, this will become false if the UDPListener ceases listening for any reason
        @Published public var listening: Bool = true
        var port: NWEndpoint.Port
        @Published var receivedWords: [Float64] = []
        @Published var isCaptureStarted = false
        //@Published var udpPacket: UDP_Packet?
        
        /// A convenience init using Int instead of NWEndpoint.Port
        convenience init(on port: Int) {
            self.init(on: NWEndpoint.Port(integerLiteral: NWEndpoint.Port.IntegerLiteralType(port)))
        }
        /// Use this init or the one that takes an Int to start the listener
        init(on port: NWEndpoint.Port) {
            self.port = port
            let params = NWParameters.udp
            params.allowFastOpen = true
            self.listener = try? NWListener(using: params, on: port)
            self.listener?.stateUpdateHandler = { update in
                switch update {
                case .ready:
                    self.isReady = true
                    print("Listener connected to port \(port)")
                case .failed, .cancelled:
                    // Announce we are no longer able to listen
                    self.listening = false
                    self.isReady = false
                    print("Listener disconnected from port \(port)")
                default:
                    print("Listener connecting to port \(port)...")
                }
            }
            self.listener?.newConnectionHandler = { connection in
                print("Listener receiving new message")
                self.createConnection(connection: connection)
            }
            self.listener?.start(queue: self.queue)
        }
        
        func createConnection(connection: NWConnection) {
            self.connection = connection
            self.connection?.stateUpdateHandler = { (newState) in
                switch (newState) {
                case .ready:
                    print("Listener ready to receive message - \(connection)")
                    self.receive()
                case .cancelled, .failed:
                    print("Listener failed to receive message - \(connection)")
                    // Cancel the listener, something went wrong
                    self.listener?.cancel()
                    // Announce we are no longer able to listen
                    self.listening = false
                default:
                    print("Listener waiting to receive message - \(connection)")
                }
            }
            self.connection?.start(queue: .global())
        }
        
        func receive() {
            self.connection?.receiveMessage { data, context, isComplete, error in
                if let unwrappedError = error {
                    print("Error: NWError received in \(#function) - \(unwrappedError)")
                    return
                }
                guard isComplete, let data = data else {
                    print("Error: Received nil Data with context - \(String(describing: context))")
                    return
                }
                
                self.handleCapturedData(data)
                if self.listening {
                    print("escutando")
                    self.receive()
                }
            }
        }
        
        func handleCapturedData(_ data: Data) {
               
           
            
            print("quantidade de dados", data.count)
            
            var value: [Float64] = []
            
            if data.count % 2 != 0 {
                
                let startOffset = 1
                let length = (data.count-1)/2
                let range = startOffset..<startOffset+length
                
                // Fatiando o Data Recebido via UDP usando o intervalo
                let slicedData = data.subdata(in: range)
                print("dados cortados:", slicedData)
                
                for i in stride(from: 0, to: slicedData.count, by: 8) {
                    let startIndex = i
                    let endIndex = min(i + 8, slicedData.count)
                    let subarray = Array(slicedData[startIndex..<endIndex])
                    let floatValue = subarray.withUnsafeBytes { $0.load(as: Float64.self) }
                    value.append(floatValue)
                    
                }
                print("Dados Recebidos", value)
                
               
                            self.receivedWords = value
                            
                       
                
            } else {
                
                let startIndex = 14
                let endIndex = data.count - 2
                let relevantData = Array(data[startIndex..<endIndex])

                
                // Iterar sobre os dados de 16 bits e convertê-los para Double
                for i in stride(from: 0, to: relevantData.count, by: 2) {
                    let byte1 = relevantData[i]
                    let byte2 = relevantData[i + 1]
                    let wordValue = UInt16(byte1) << 8 | UInt16(byte2)
                    let doubleValue = Double(wordValue)
                    value.append(doubleValue)
                }

              
                            self.receivedWords = value
                            
                        
                
                print("Dados Recebidos 2", value)
                 
                }
            }
        
        func cancel() {
            self.listening = false
            self.isCaptureStarted = false
            self.connection?.cancel()
            
        }
        
        
    }
Answered by DTS Engineer in 764534022

Network framework is not able to handle all possible UDP broadcast architectures, something I called out in TN3151 Choosing the right networking API:

Network framework supports UDP multicast using the NWConnectionGroup class, but that support has limits and, specifically, it does not support UDP broadcast. If you need something that’s not supported by Network framework, use BSD Sockets.

To work with UDP broadcasts or multicasts on iOS, you need to apply for, and be granted access to, the multicast networking managed entitlement. For more on that whole issue, see Local Network Privacy FAQ.

I don't have a paid Apple Developer license yet

That will be a problem; there’s no way to get that entitlement when using free provisioning (aka a Personal Team).

What are you trying to do with broadcasts? I see a lot of folks go down this path for the wrong reasons [1].

Share and Enjoy

Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"

[1] The most common wrong reason is that they’re implementing a custom service discovery protocol. In most cases you’re better off using Bonjour.

The second wrong reason is that they think it’ll be faster. Due to the way that Wi-Fi works, that’s often not the case. For more on this, see Wi-Fi Fundamentals.

I've already checked Wireshark and the packages are being sent correctly, the problem is when I run in the simulator and directly on the iphone that the errors appear.

Here are the log errors running on the Iphone:

Listener receiving new message
Listener waiting to receive message - [C1 connected 192.168.0.104:60085 udp, local: 0.0.0.0:50200, fast-open, definite, attribution: developer, server, path satisfied (Path is satisfied), viable, interface: en0[802.11], ipv4, dns]
Listener ready to receive message - [C1 connected 192.168.0.104:60085 udp, local: 0.0.0.0:50200, fast-open, definite, attribution: developer, server, path satisfied (Path is satisfied), viable, interface: en0[802.11], ipv4, dns]
849 bytes
escutando
2023-09-06 09:44:39.308418-0300 SMAD[38636:10401791] [] nw_path_evaluator_create_flow_inner NECP_CLIENT_ACTION_ADD_FLOW FCD9AC7A-2C3A-44EC-8FDE-0DA79453DCE0 [17: File exists]
2023-09-06 09:44:39.308451-0300 SMAD[38636:10401791] [connection] nw_endpoint_flow_setup_channel [C2 192.168.0.104:60085 initial channel-flow (satisfied (Path is satisfied), viable, interface: en0[802.11], ipv4, dns)] failed to request add nexus flow
2023-09-06 09:44:39.308893-0300 SMAD[38636:10401791] [connection] nw_endpoint_handler_create_from_protocol_listener [C2 192.168.0.104:60085 failed channel-flow (satisfied (Path is satisfied), viable, interface: en0[802.11], ipv4, dns)] nw_endpoint_flow_pre_attach_protocols
2023-09-06 09:44:39.308950-0300 SMAD[38636:10401791] [connection] nw_connection_create_from_protocol_on_nw_queue [C2] Failed to create connection from listener
2023-09-06 09:44:39.308975-0300 SMAD[38636:10401791] [] nw_ip_channel_inbox_handle_new_flow nw_connection_create_from_protocol_on_nw_queue failed
2023-09-06 09:44:39.407738-0300 SMAD[38636:10401791] [] nw_path_evaluator_create_flow_inner NECP_CLIENT_ACTION_ADD_FLOW FCD9AC7A-2C3A-44EC-8FDE-0DA79453DCE0 [17: File exists]
2023-09-06 09:44:39.407770-0300 SMAD[38636:10401791] [connection] nw_endpoint_flow_setup_channel [C3 192.168.0.104:60085 initial channel-flow (satisfied (Path is satisfied), viable, interface: en0[802.11], ipv4, dns)] failed to request add nexus flow
2023-09-06 09:44:39.407828-0300 SMAD[38636:10401791] [connection] nw_endpoint_handler_create_from_protocol_listener [C3 192.168.0.104:60085 failed channel-flow (satisfied (Path is satisfied), viable, interface: en0[802.11], ipv4, dns)] nw_endpoint_flow_pre_attach_protocols

UDP packets are sent by a PC connecting to a Wifi router and my Iphone is also connecting to it and listening on the correct port.

I’d like to clarify the on-the-wire behaviour here:

  • Do these datagrams make up a UDP flow? That is, do they all have the same local IP, local port, remote IP, remote port tuple?

  • If so, what’s a typical example of those values?

  • If not, what varies from datagram to datagram?

Share and Enjoy

Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"

Before I answer your questions, I may have found the solution. I did some more tests using unicast (192.168.0.101) and it worked both in the simulator and directly on the iPhone. Do these errors appear because I used broadcast (192.168.0.255) and did not do Request multicast entitlement from Apple? I don't have a paid Apple Developer license yet, so I can't make this request? Or just add the Capabilities over Network Multicast? I'm a student and I'm learning now, sorry for any mistakes

Accepted Answer

Network framework is not able to handle all possible UDP broadcast architectures, something I called out in TN3151 Choosing the right networking API:

Network framework supports UDP multicast using the NWConnectionGroup class, but that support has limits and, specifically, it does not support UDP broadcast. If you need something that’s not supported by Network framework, use BSD Sockets.

To work with UDP broadcasts or multicasts on iOS, you need to apply for, and be granted access to, the multicast networking managed entitlement. For more on that whole issue, see Local Network Privacy FAQ.

I don't have a paid Apple Developer license yet

That will be a problem; there’s no way to get that entitlement when using free provisioning (aka a Personal Team).

What are you trying to do with broadcasts? I see a lot of folks go down this path for the wrong reasons [1].

Share and Enjoy

Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"

[1] The most common wrong reason is that they’re implementing a custom service discovery protocol. In most cases you’re better off using Bonjour.

The second wrong reason is that they think it’ll be faster. Due to the way that Wi-Fi works, that’s often not the case. For more on this, see Wi-Fi Fundamentals.

I work with flight tests, and we already have some Android applications working on the system. On the aircraft we have a data acquisition system that sends UDP packets with flight data to a router. As we have several tablets connected at the same time, we had to use broadcast so that everyone could receive these packets. The Android App is already developed and operational. I'm developing a copy for IOS. I used BSD Sockets and it worked, but it was using a lot of the processor rate (46% when receiving packets), while using the Network Framework this rate is 3%, so I was insisting on trying to apply this instead of BSD. Given what you told me, I will apply BSD to continue developing the APP, and in the future for the license to use all the resources and improve the app. Thank you for your help.

As we have several tablets connected at the same time, we had to use broadcast so that everyone could receive these packets.

Bandwidth wise, it’d probably be better for each of the devices to open their own connection [1] to the accessory and have it unicast the packets to the device. Broadcasts on Wi-Fi are slow.

I used BSD Sockets and it worked, but it was using a lot of the processor rate (46% when receiving packets)

Something else is going on here. BSD Sockets can be slower than Network framework, but I wouldn’t expect a disparity like that [2].

Share and Enjoy

Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"

[1] I’d probably use a TCP connection for this, but if you want to stick with UDP then you can use a UDP flow.

[2] Unless you’re processing a bazillion packets per second, but that seems unlikely in this case.

UDP listener only receives one packet and show Errors
 
 
Q