I'm struggling to use NWListener and NWConnection on the same port.I have a simple task requires that I send out a UDP datagram to the broadcast address, and expect to receive replies on the port which sent the datagram. In Blue Sockets, I can create a socket, send out the broadcast message and listen and accept connections on the same socket.In wireshark the exchange looks like:10.0.0.123 255.255.255.255 UDP 71 62003 -> 9000 Len=29
10.0.0.202 10.0.0.123 UDP 1060 9000 -> 62003 Len=101However after hours of experimentation in a swift playground, I am not able to create an NWListener and an NWConnection on the same socket.Here are the salient pieces of code:let udpListener = try! NWListener(using: .udp)
... setup and start listener
let broadcast: NWEndpoint.Host = "255.255.255.255"
let portUDP: NWEndpoint.Port = 9000
let localPort : NWEndpoint.Port = udpListener!.port!
let localEndpoint = NWEndpoint.hostPort(host: "10.0.0.123", port: localPort)
let parameters = NWParameters.udp
parameters.requiredLocalEndpoint = localEndpoint
let connection = NWConnection(host: broadcast, port: portUDP, using: parameters)However creating the NWConnection on the same port as the NWListener gives an error that the port is in use. I've tried the reverse, createing the connection on a known port, closing it and starting a listener immediately affterwards on that same port, but that also results in an error.How can I implement this very simple protocol using the Network API ? Are there some options I need to set ?Thanks for any help.Guy
Post
Replies
Boosts
Views
Activity
The following NWConnection code sending a UDP broadcast message works perfectly on Catalina, but fails with EACESS on Mojave. I am guessing that the socket broadcast flag is set on Catalina but not Mojave, is there a workaround to get this function on 10.14, or is broadcast in the Network framework broken in Mojave ?import Network
import PlaygroundSupport
PlaygroundPage.current.needsIndefiniteExecution = true
var broadcastConnection: NWConnection?
func broadcast() {
let hostUDP: NWEndpoint.Host = "255.255.255.255"
let portUDP: NWEndpoint.Port = 9000
broadcastConnection = NWConnection(host: hostUDP, port: portUDP, using: .udp)
broadcastConnection?.stateUpdateHandler = { (newState) in
switch newState {
case .ready:
let packet = "Hello".data(using: .utf8)!
print("SEND ")
broadcastConnection?.send(content: packet, completion: NWConnection.SendCompletion.contentProcessed(({ (NWError) in
print("Sent: \(NWError?.debugDescription ?? "Success")")
broadcastConnection?.cancel()
broadcastConnection = nil
PlaygroundPage.current.finishExecution()
})))
default:
break
}
}
broadcastConnection?.start(queue: .global())
}
broadcast()Result on Catalina:SEND
Sent: SuccessResult on Mojave:SEND
Sent: POSIXErrorCode: Permission deniedThanks for any insight CheersGuy
When creating an NWConnectionn on a specific local port, the local IP address also needs to be specified. However there does not seem to be any clean API for discovering what the default NWConnection IP address will be.In fact getting the main local IP address on macOS or iOS does not seem to be straightforward at all.I found a workaround which scarily relies on the debug description for a network connection. Is there a better, more robust way, more in line with the Network API ?import Network
import PlaygroundSupport
class IPHelper {
static var dummy: NWConnection?
static func getIPThen(callOnMain: @escaping (NWEndpoint.Host) -> Void ) {
guard dummy == nil else {
return
}
dummy = NWConnection(host: "255.255.255.255", port: 9000, using:.udp)
dummy?.stateUpdateHandler = {(newState) in
switch (newState) {
case .ready:
let ip = self.dummy?.currentPath?.localEndpoint?.debugDescription.split(separator: ":").first
self.dummy?.cancel()
self.dummy = nil
DispatchQueue.main.async {
callOnMain(NWEndpoint.Host("\(ip ?? "")"))
}
default:
break
}
}
dummy?.start(queue: .global())
}
}
PlaygroundPage.current.needsIndefiniteExecution = true
IPHelper.getIPThen { (ip) in
let address = ip
}