I have the following code running fine when receiving direct messages via UDP:
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: [])
var connections = [NWConnection]()
override func viewDidLoad() {
super.viewDidLoad()
myOnButton(self)
}
@IBAction func myOnButton(_ sender: Any) {
guard self.udpListener == nil else {
print(" ???? Already listening. Not starting again")
return
}
do {
self.udpListener = try NWListener(using: .udp, on: 2000)
self.udpListener?.stateUpdateHandler = { (listenerState) in
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")
for connection in self.connections {
connection.cancel()
}
self.udpListener = nil
default:
break;
}
}
self.udpListener?.start(queue: backgroundQueueUdpListener)
self.udpListener?.newConnectionHandler = { (incomingUdpConnection) in
print ("???? New connection (incomingUdpConnection.debugDescription)")
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.connections.append(incomingUdpConnection)
self.processData(incomingUdpConnection)
case .failed(let error):
print("Connection: failed: (error)")
self.connections.removeAll(where: {incomingUdpConnection === $0})
case .cancelled:
print("Connection: cancelled")
self.connections.removeAll(where: {incomingUdpConnection === $0})
default:
break
}
}
incomingUdpConnection.start(queue: self.backgroundQueueUdpConnection)
}
} catch {
print("????")
}
}
@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)")
}
}
if error == nil {
self.processData(incomingUdpConnection)
}
})
}
}
Sending endless UDP packages directly to the device like this:
echo -n "Testdata" | socat - udp-datagram:192.168.2.126:2000,broadcast,sourceport=2000
works as expected, and all messages arrive:
Listener: Waiting POSIXErrorCode: Network is down
Listener: Ready and listens on port: 2000
???? New connection [C1 192.168.2.134:2000 udp, local: 192.168.2.126:2000, indefinite, server, path satisfied (Path is satisfied), interface: en0, scoped, ipv4, ipv6, dns]
Connection: ready
DATA = Testdata
DATA = Testdata
DATA = Testdata
DATA = Testdata
...
But trying the same with broadcast messages
echo -n "Testat" | socat - udp-datagram:255.255.255.255:2000,broadcast,sourceport=2000
results in only receiving the first message, and a new connection with each received UDP package.
Listener: Waiting POSIXErrorCode: Network is down
Listener: Ready and listens on port: 2000
???? New connection [C1 192.168.2.134:2000 udp, local: 0.0.0.0:2000, indefinite, server, path satisfied (Path is satisfied), interface: en0, ipv4, ipv6, dns]
Connection: ready
DATA = Testdata
2020-06-28 14:22:03.668116+0200 networking[25440:13489733] [] nw_channel_reclassify_input reclassify failed, could not find client for slot 77926755-A281-4AFD-9649-92FBD1A21FA6
???? New connection [C2 192.168.2.134:2000 udp, local: 0.0.0.0:2000, indefinite, server, path satisfied (Path is satisfied), interface: en0, ipv4, ipv6, dns]
2020-06-28 14:22:04.384443+0200 networking[25440:13489733] [] nw_channel_reclassify_input reclassify failed, could not find client for slot 7EAC98FA-F665-43C1-9B15-B68B74A56BBC
???? New connection [C3 192.168.2.134:2000 udp, local: 0.0.0.0:2000, indefinite, server, path satisfied (Path is satisfied), interface: en0, ipv4, ipv6, dns]
2020-06-28 14:22:05.096808+0200 networking[25440:13489733] [] nw_channel_reclassify_input reclassify failed, could not find client for slot AA3EE3BA-D891-4D07-87AF-0CB0A9382CDF
???? New connection [C4 192.168.2.134:2000 udp, local: 0.0.0.0:2000, indefinite, server, path satisfied (Path is satisfied), interface: en0, ipv4, ipv6, dns]
2020-06-28 14:22:05.714651+0200 networking[25440:13489733] [] nw_channel_reclassify_input reclassify failed, could not find client for slot ED108E8B-E9BC-43C3-8AA7-B4BD0515EB54
Workaround would be to .cancel the connection after each received package, but I doubt this is the way how this was intended to be used to receive UDP broadcast messages. So what is the correct approach to receive UDP broadcast packages in an endless loop?
1
Ok, I got sort of an answer.
I had the same basic issue in Objective-C. I took the step of cancelling the connection in the handler, which sort of works, but you end up with data loss – about 30% in my experience – as the stream fails to pick up broadcast data.
However, I did get feedback from dts, which is basically this – Networking Library is not designed for receiving broadcast UDP.
I’ve reverted to BSD sockets.