Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to resolve addresses and port information from an NWEndpoint.service enum case (if possible)

Currently I'm using NetServiceBrowser to find Bonjour services and to resolve corresponding addresses and port.

Looking to de-complicate my code I stumbled upon NWBrowser which seems to provide a very simple interface to deal with the Bonjour discovering.

However, the browseResultsChangedHandler sends back results and changes which contain an endpoint of enum case service. I'm trying to get address and port information from the results, but is seems the NWEndpoint would have to be of enum type .hostPort.

Ideally I would use the endpoint to connect to servers using NWConnection, however, I'm using another library which doesn't handle the NWEndpoint directly.

Are there (simple) ways of getting addresses and port information from an NWEndpoint.service result?

import Foundation
import Network

let browser = NWBrowser(for: .bonjour(type: "_http._tcp", domain: ""), using: NWParameters())

browser.browseResultsChangedHandler = { (results, changes) in
    print("Results:")

    for result in results
    {
        if case .service(let service) = result.endpoint
        {
            debugPrint(service)
        }
        else
        {
            assert(false, "This nevers gets executed")
        }
    }

    print("Changes:")

    for change in changes
    {
        if case .added(let added) = change
        {
            if case .service(let service) = added.endpoint
            {
                debugPrint(service)
            }
            else
            {
                assert(false, "This nevers gets executed")
            }
        }
    }
}

browser.start(queue: DispatchQueue.main)

sleep(3)
like image 855
Ruurd Adema Avatar asked Mar 07 '20 16:03

Ruurd Adema


1 Answers

This is possible.

First, you should not attempt to get a service's host and port until the user has actually chosen to connect to it - in most of your application, you should only store a reference to the service object, since Bonjour allows the service's host and port to change. From Bonjour Concepts:

Additionally, services are not tied to specific IP addresses or even host names. […] If clients store the host name (as in most cases they now do), they will not be able to connect if the service moves to a different host.

Bonjour takes the service-oriented view. Queries are made according to the type of service needed, not the hosts providing them. Applications store service instance names, not addresses, so if the IP address, port number, or even host name has changed, the application can still connect. By concentrating on services rather than devices, the user’s browsing experience is made more useful and trouble-free.

This reduces DNS noise within your network and is core to the design of Bonjour. This means all my next suggestions should not happen in your browseResultsChangedHandler.

The officially recommended way to use NWBrowser appears to be to open a NWConnection to the service and use that instead of extracting the address and port and connecting manually. You can open the connection and the Network framework will handle resolving the actual host/port and connecting to the service.

let connection = NWConnection(to: service.endpoint, using: .tcp)

connection.stateUpdateHandler = { state in
    switch state {
    case .ready:
        if let innerEndpoint = connection.currentPath?.remoteEndpoint,
           case .hostPort(let host, let port) = innerEndpoint {
            print("Connected to", "\(host):\(port)") // Here, I have the host/port information
        }
    default:
        break
    }
}
connection.start(queue: .global())

If you can't use this Network framework connection, I'd guess you could then close the connection (maybe need to wait for TCP to stop using the port) and use it for your own purposes.

You can also use the deprecated NetService api to resolve a service (although I haven't personally gotten this to work with a manually constructed instance) or DNSServiceResolve.

There's a lot more context from an official Apple representative in this Apple Developer Forum thread.

like image 121
Cameron Little Avatar answered Nov 11 '22 19:11

Cameron Little