Foundation Network Services Architecture
The architecture for Bonjour network services in the Foundation framework is designed so that you do not need to understand the details of DNS record management, instead letting you manage services in terms of the four fundamental operations defined for Bonjour network services:
Advertising a service, a process known as publication
Browsing for available services, also called service discovery
Connecting to services directly (the preferred technique)
Resolving services (translating service names into something that lets you connect to them)
Foundation Service Discovery Classes
To support these operations, the Foundation framework defines two classes—one that you use to browse for services and domains, and one that represents an individual service.
NSNetServiceBrowser class has two purposes: browsing for services and browsing for Bonjour domains. At any given time, a single
NSNetServiceBrowser object can execute at most one search operation; if you need to search for multiple types of services at once, use multiple
When browsing for a specific type of service—for example, searching for FTP services on the local network—this class provides you with filled-out instances of
NSNetService for each remote service.
When browsing for domains, whether for registration or browsing, this class provides you with a list of available domain names. (Note that domain browsing is outside the scope of this chapter. For step-by-step instructions and code examples about domain browsing, and guidance about how to use browsing in your code, read Browsing for Domains.)
NSNetService class represents a single service (either a remote service that your app wants to use or a local service that your app is publishing). Instances of this class are involved in all of the Bonjour operations described in this chapter.
NSNetService objects that represent remote services come preinitialized with the service name, type, and domain, but no host name, IP address or port number. The name, type, and domain are used to resolve the instance into the information needed to connect to the service (IP addresses and port number) upon request.
NSNetService objects used for publishing local services to the network must be initialized (by your code) with the service name, type, domain, and port number. Bonjour uses this information to announce the availability of your service over the network.
Theory of Operation
Because network discovery can take a long time to complete, the
NSNetServiceBrowser classes perform all their operations asynchronously. The methods provided by
NSNetServiceBrowser return immediately. As a result, your app can continue executing while network operations take place.
Both classes require delegate objects in your app that must implement appropriate methods to handle the resulting data. In some cases, these classes may call your delegate methods more than once, providing additional data with each call.
NSNetService class handles service publication. To publish a service:
The app sets up a socket and begins listening for incoming connections on that socket, as described in Networking Programming Topics.
The app initializes an
NSNetServiceobject, providing the port number, the name of the service, and domain information, and then sets up a delegate object to receive results. The object automatically schedules itself with the current run loop.
The app sends a message to the
NSNetServiceobject, requesting that the service be published to the network, and publication proceeds asynchronously.
NSNetServiceobject calls its delegate with information about the status of the publication.
When the service has been successfully published, the delegate is notified. If publication fails for any reason, the delegate is also notified, along with appropriate error information. If publication proceeds successfully, no further messages are sent to the delegate.
For step-by-step instructions and code examples about service publication, see Publishing Network Services.
To discover services advertised on the network, you use the
NSNetServiceBrowser class, as shown in Figure 1-1.
In step 1, the app initializes an
NSNetServiceBrowser object and associates a delegate object with it. In step 2, the
NSNetServiceBrowser object searches asynchronously for services. In step 3, results are returned to the delegate object in the form of
Step 3 usually occurs multiple times. Initially, the browser calls you once for each service that it currently knows about. When it calls your delegate method with the last of these currently known services, it passes
NO for the
However, because the browser object continues to browse until your app explicitly tells it to stop, your delegate learns about new services as they come online and learns about the disappearance of services as they shut down. As a result, your
didFindService:moreComing: method may be called with new services even after you get a
moreComing value of
For step-by-step service discovery instructions and sample code snippets, read Browsing for Network Services.
Connecting to Services
To connect to a Bonjour-advertised service, an app typically calls a method such as
getInputStream:outputStream:, which provides a stream or pair of streams for communicating with the service (as shown in Figure 1-2). When the app opens that stream, the stream object itself connects to the Bonjour service in the same way that it would connect if you had passed it a hostname. The stream object, in turn, uses the
NSNetService object to perform DNS lookups on its behalf.
Although the steps above describe the recommended way to connect to a Bonjour-advertised service, you can also connect in two other ways:
By resolving the
NSNetServiceobject, and then asking the
NSNetServiceobject to provide the service’s hostname, then passing that hostname to a connect-by-name API.
By asking the
NSNetServiceobject to resolve the service to a set of IP addresses (as described in Resolving Services Manually), and then attempting to connect to the host yourself. This technique is discouraged, however, because of the complexity caused by multihoming.
For step-by-step service connection instructions and sample code snippets, read Connecting to and Monitoring Network Services.
Resolving Services Manually
In the most common usage, resolution is performed transparently, either when you connect to a service by calling a connect-to-service method such as
getInputStream:outputStream: or when you later reconnect to a service using a name obtained by calling
hostName on an already resolved service object.
If for some reason you need to know the actual IP addresses for a service, you can also ask the object to resolve a service name explicitly. To prevent your app from slowing down, resolution takes place asynchronously, returning results or error messages to the
NSNetService object’s delegate. If valid addresses were found for the service, your app can use them to make a socket connection.
Figure 1-3 illustrates this process.
In step 1, the app either explicitly initializes or otherwise obtains an
NSNetService instance for the service—in this case a local
music service over TCP called
Ed's Party Mix. In step 2, the
NSNetService object receives a
resolve message. The resolution proceeds asynchronously, and at some point it receives an IP address and port number for the service (
169.254.150.84:1010). In step 3, the delegate is notified, and in step 4, the delegate asks the
NSNetService object for a list of addresses and a port number. A service can have multiple IP addresses, but always has exactly one port.
For step-by-step service resolution instructions and sample code snippets, read Connecting to and Monitoring Network Services.