I'm using the Network framework to perform SSDP discovery for UPnP devices. The multicast entitlement is set, and discovery is working fine, except when another application is also using port 1900. In that case the following errors are given (this is in a catalyst app on macOS, the behavior on a physical iPhone is the same):
[] nw_path_evaluator_evaluate NECP_CLIENT_ACTION_ADD error [48: Address already in use]
[] nw_path_create_evaluator_for_listener nw_path_evaluator_evaluate failed
[listener] nw_listener_start_on_queue [L1] nw_path_create_evaluator_for_listener failed
[connection_group] nw_connection_group_handle_listener_state_change [G1] listener failed with error
Is it possible that multiple applications connect on the same port?
Here's the relevant code from the discovery class I'm using:
public class SSDPDiscovery {
enum SSDPMessageType {
case searchResponse
case availableNotification
case updateNotification
case unavailableNotification
}
private let multicastGroupAddress = "239.255.255.250"
private let multicastUDPPort: UInt16 = 1900
private var multicastGroup: NWMulticastGroup?
private var connectionGroup: NWConnectionGroup?
private var types = [String]()
func startDiscovery(forTypes types: [String]) throws {
guard multicastGroup == nil else { throw UPnPError.alreadyConnected }
let multicastGroup = try NWMulticastGroup(for:[.hostPort(host: .init(multicastGroupAddress), port: .init(integerLiteral: multicastUDPPort))])
let params = NWParameters.udp
params.allowLocalEndpointReuse = true
let connectionGroup = NWConnectionGroup(with: multicastGroup, using: params)
connectionGroup.stateUpdateHandler = { [weak self] (newState) in
Logger.swiftUPnP.debug("Connection group entered state \(String(describing: newState))")
switch newState {
case let .failed(error):
Logger.swiftUPnP.error("\(error.localizedDescription)")
case .cancelled:
self?.multicastGroup = nil
self?.connectionGroup = nil
default:
break
}
}
connectionGroup.setReceiveHandler(maximumMessageSize: 65535, rejectOversizedMessages: true) { (message, content, isComplete) in
if let content = content {
self.processData(content)
}
}
connectionGroup.start(queue: .main)
self.types = types
self.multicastGroup = multicastGroup
self.connectionGroup = connectionGroup
}
func stopDiscovery() {
guard let connectionGroup = connectionGroup else { return }
connectionGroup.cancel()
multicastGroup = nil
self.connectionGroup = nil
types = []
}
}