I would like to receive some data via UDP in an endless loop until the user cancels the data receiving. I created an NWListener, and set it up. It receives data as expected. However once I call .cancel() on my NWListener it changes the state to .cancelled but afterwards I have no chance to restart the listening process again. It blocks somehow the port. I would be able to restart it on another port, but this is not what I want to do. On the same port it ends up in this error message:
2020-02-20 23:00:28.957501+0100 networking[10942:7110395] [] nw_path_evaluator_evaluate NECP_CLIENT_ACTION_ADD error [48: Address already in use]
2020-02-20 23:00:28.957642+0100 networking[10942:7110395] [] nw_path_create_evaluator_for_listener nw_path_evaluator_evaluate failed
2020-02-20 23:00:28.957735+0100 networking[10942:7110395] [] nw_listener_start_locked [L2] nw_path_create_evaluator_for_listener failed
NWListener Handler called
Listener: Failed POSIXErrorCode: Address already in use
The error only occurs on real devices (im simulator it works as expected), and only if the real device has either recieved at least one UDP message, or after starting the NWListener twice without .cancel() it before.
I created a dedicated small sample project in Xcode to nail the issue down and to ensure it has nothing to do with the rest of my actual project. This is the ViewController I created to show the problem, I just connected two buttons to start/stop UDP listening:
import UIKit
import Network
class ViewController: UIViewController {
var udpListener:NWListener?
var backgroundQueueUdpListener = DispatchQueue(label: "udp-lis.bg.queue", attributes: [])
var backgroundQueueUdpConnection = DispatchQueue(label: "udp-con.bg.queue", attributes: [])
override func viewDidLoad() {
super.viewDidLoad()
myOnButton(self)
}
@IBAction func myOnButton(_ sender: Any) {
do {
self.udpListener = try NWListener(using: .udp, on: 55555)
self.udpListener?.stateUpdateHandler = { (listenerState) in
print(" NWListener Handler called")
switch listenerState {
case .setup:
print("Listener: Setup")
case .waiting(let error):
print("Listener: Waiting \(error)")
case .ready:
print("Listener: Ready and listens on port: \(self.udpListener?.port?.debugDescription ?? "-")")
case .failed(let error):
print("Listener: Failed \(error)")
case .cancelled:
print("Listener: Cancelled by myOffButton")
default:
break;
}
}
self.udpListener?.start(queue: backgroundQueueUdpListener)
self.udpListener?.newConnectionHandler = { (incomingUdpConnection) in
print(" NWConnection Handler called ")
incomingUdpConnection.stateUpdateHandler = { (udpConnectionState) in
switch udpConnectionState {
case .setup:
print("Connection: setup")
case .waiting(let error):
print("Connection: waiting: \(error)")
case .ready:
print("Connection: ready")
self.processData(incomingUdpConnection)
case .failed(let error):
print("Connection: failed: \(error)")
case .cancelled:
print("Connection: cancelled")
default:
break
}
}
incomingUdpConnection.start(queue: self.backgroundQueueUdpConnection)
}
} catch {
print(" CATCH")
}
}
@IBAction func myOffButton(_ sender: Any) {
udpListener?.cancel()
}
func processData(_ incomingUdpConnection :NWConnection) {
incomingUdpConnection.receiveMessage(completion: {(data, context, isComplete, error) in
if let data = data, !data.isEmpty {
if let string = String(data: data, encoding: .ascii) {
print ("DATA = \(string)")
}
}
//print ("context = \(context)")
print ("isComplete = \(isComplete)")
//print ("error = \(error)")
self.processData(incomingUdpConnection)
})
}
}
I hope someone can help me, I spent now several hours to solve this issue, but without any success so far.
To test incoming messages via UDP, I used this shell script:
#!/bin/zsh
while :
do
echo "1234" | nc -cu -w 0 192.168.2.106 55555
sleep 1
echo "567890" | nc -cu -w 0 192.168.2.106 55555
sleep 1
done