I have an application that needs to search and resolve a Bonjour-advertised service whose name is known in advance. Most Bonjour examples I have found related to service discovery are structured more or less like this:
- Call
browse
to detect all services of a given type (for example, this could be_http._tcp
) - For each service found,
serviceFound
is called. Service names are reported here - Call
resolve
on each service found - For each service resolved,
serviceResolved
is called
Is it possible with Bonjour to skip the "discovery" stage, since I know in advance the name of the service I want to resolve? Can I just detect and resolve a service with a known name?
1- Answer
Yes, you can start with the 3rd step if you already know the name of the service. This is because this step is performed through a DNS lookup for a SRV record with the name of the service sent to a well-known multicast address. So, no previous information is needed to make this call, and the mDNS responder must be stateless, since the underlying DNS protocol is stateless (each response is bound to a unique request - no state maintained between several requests).
2- Example
Here is an example I've just written with Swift, that has passed tests running on my iPad to find a service running on my Mac Mini. So, we suppose the domain is
local
, the service type is_http._tcp
and the name of the service ismyservice
, running on host Mac-mini-de-Alexandre.local and listening to TCP port 8080.To track informations about the service, for instance its hostname and TCP port, we define a class that implements the NetServiceDelegate protocol:
This new class will be used to instantiate the delegate for a NetService instance.
So, we create a NetService instance corresponding to the service we already know about, that we store in the long-term with a static constant property of some main class:
It is stored in the long term because it must not be deallocated before we find our service.
Note that the delegate property in class NetService is declared unowned(unsafe). So, we also need to create a reference to the delegate instance:
When we want to resolve the service, we may write:
The delegate instance will be called later (
resolve()
is a non-blocking method) if the service is found, and in this case, it will print the hostname and port.Here is the output I got in my Xcode output window:
Finally, note that because of the unowned reference, it would be a mistake to write the following code (the delegate instance would be shortly deallocated):
3- Trick to help debugging
Here is a little trick to debug such a mDNS resolution: on a Unix shell (macOS for instance), just run the following line:
If an http service with name myservice is running, you will get the host name and port. With my example, you will get the following:
So, before trying to use the Swift code I've written here, just check that your service is correctly announced with this shell command.
Finally, note that this dig based command only makes one IPv4 mDNS query on each IPv4 network interface, but using the Apple Bonjour API, two groups of mDNS requests are done automatically: one with IPv4 to the multicast destination 224.0.0.251 on each network interface that supports IPv4, and another with IPv6 to the multicast destination ff02::fb on each interface that supports IPv6.