Rendezvous Network Services

Rendezvous is Apple’s implementation of zero-configuration networking (Zeroconf). Introduced with the Jaguar release of Mac OS X, Rendezvous brings AppleTalk’s ease of use to standard IP networking. This gives the user the ability to browse for printing, file sharing, or any other IP-based services much as was done with the Chooser in earlier versions of the Mac OS.

Tip

For more information on Rendezvous networking, see Apple’s Rendezvous developer page at http://developer.apple.com/macosx/rendezvous/, or visit the Zeroconf working group web site at http://www.zeroconf.org.

Foundation provides access to Rendezvous’ low-level APIs through the classes NSNetService and NSNetServiceBrowser. An application registers, or publishes, a service on the network by using NSNetService. The class NSNetServiceBrowser searches for and discovers services that are registered elsewhere. This section provides an overview of how these classes fit into an application.

NSNetService represents a network service that applications either publish or use as a client. A network service can be FTP, Telnet, SSH, HTTP, or something of your own design.

The Picture Sharing application included with the Foundation example code found in the Developer Tools installation (/Developer/Examples/Foundation/PictureSharing) is an example of a custom Rendezvous service. This example has two applications: a server application that publishes a picture sharing service and a client application that browses for picture sharing services.

To set up NSNetService, you must do the following:

Before a service can be published, you must first create a network socket to which clients connect to access the service. While the Net Services API (which collectively refers to NSNetService and NSNetServiceBrowser) has little to do with data transfer between hosts, it has everything to do with advertising a service’s existence on a local network. On Mac OS X, several APIs can create a socket: NSSocketPort provides an Objective-C API for sockets, CoreFoundation provides the CFSocket API, and Darwin has the BSD Sockets API. Since this book focuses on how to accomplish tasks with Cocoa, a discussion of the use of NSSocketPort is relevant and will be discussed later, while the Unix and CoreFoundation APIs are beyond the scope of this book.

NSNetService has two initializers:

NSNetService’s publication-appropriate initializer is initWithDomain:type:name:port:. The first argument to this method is the domain in which the service is registered. As of Mac OS X 10.2, only the local registration domain, .local., is supported. Since the Zeroconf working group is still hammering out the details of zero-configuration networking, the local domain may not always be .local. As such, passing an empty string to initWithDomain: is preferable to passing .local. as the domain. Doing so tells NSNetService to register the services under the default domain.

The type: argument specifies the service type and transport protocol. This string takes the form _servicetype._tcp., where _servicetype can be any standard service such as HTTP, FTP, or Telnet, or it may be an arbitrary service type specific to your application, such as _myservice.

The third argument, name:, is the human-readable name for the service displayed in service browser lists. It can be any UTF8 string you like. Finally, port: is the port number to which the service socket is bound. To publish a service, invoke NSNetService’s publish method. To remove a service from the network, send a stop message to the service instance. Example 6-5 shows how an application can create and publish a service.

NSNetService tells interested parties that the designated host has a socket listening for connections on the specified port. NSNetService multicasts the address and port information for an open socket to the network. The response message from the Rendezvous host contains additional information that identifies the service name and type. Whether or not a socket is listening on that port is another question; NSNetService puts us on the honor system to make sure everything is configured properly.

The Net Services API uses delegation to drive an application’s user interface and for error handling. Setting the delegate of an instance of NSNetService or NSNetServiceBrowser is essential for using these classes effectively.

NSNetService declares that the delegates should implement the following methods:

netServiceWillPublish:

Notifies the delegate that the service is about to be published. The NSNetService that invoked this method is passed in the argument.

netService:didNotPublish:

Notifies the delegate of an error that occurred while attempting to publish the service. The netService: argument is the NSNetService object that produced the error, and the didNotPublish: argument is an error dictionary containing information about the error. The dictionary contains objects for the keys NSNetServiceErrorCode and NSNetServiceErrorDomain.

netServiceWillResolve:

Invoked in the delegate when the network is ready to resolve the service. This method is invoked only after sending a resolve message to the service object. The argument is the NSNetService that received the resolve message.

netService:didResolve:

Notifies the delegate that the service was successfully resolved and is now ready to use. At this point, the address used to connect to the service has been verified and is ready to use.

netService:didNotResolve:

If an error occurs while attempting to resolve a service, the delegate is notified via this method. The netService: argument is the service instance that produced the error, and the didNotResolve: argument contains the error dictionary. The dictionary keys NSNetServiceErrorCode and NSNetServiceErrorDomain provide information about the error.

netServiceDidStop:

This method is invoked in the delegate when invoking the stop method in an NSNetService that previously received a publish or resolve message.

Instances of NSNetService either publish or resolve a service, and thus far this chapter has only shown how to publish a service. Services meant for publication can’t be used for resolution, and those meant for resolution can’t be used for publication.

The delegate methods of NSNetService reflect this division. The three methods netServiceWillResolve:, netService:didResolve:, and netService:didNotResolve: notify the delegate of the status of a service resolution request (in response to a resolve message). The remaining methods, netServiceWillPublish:, netService: didNotPublish:, and netServiceDidStop:, notify the delegate of a publish operation’s status. Services meant for publication do not invoke resolution-specific delegate methods, and those services meant for resolution won’t ever invoke publication-specific delegate methods.

Implementing these delegate methods is not necessary for a functioning instance of NSNetService. Net services can be published and resolved without having been assigned a delegate; however, without a delegate, there is no way of knowing a particular net service object’s status.

The NSNetServiceBrowser class is an implementation of Rendezvous’ service discovery protocol. This class depends heavily on a delegate, which is the only means of alerting an application to the discovery of a service. NSNetServiceBrowser searches for domains as well as services and uses the same mechanisms in the delegate object to report discovered domains. The methods that a delegate of NSNetServiceBrowser may implement are as follows:

Several of the delegate methods for NSNetServiceBrowser listed here include the moreComing: argument. This argument contains a BOOL value indicating whether or not more services are to be reported (NSNetServiceBrowser may discover services faster than they can be reported). The utility of this flag has to do with how the information is reported through the user interface. The idea is that the user interface should be updated with all available information in one fell swoop. Rather than adding a service name to the browser list every time one is discovered, the delegate method should update the interface only if moreComing: reports back with NO, saying that there are no further services to report at the time.

NSNetServiceBrowser searches for domains and for services. The previous section discussed the service discovery aspect of NSNetServiceBrowser. When searching for domains, you can look for either all domains using the searchForAllDomains method or only for those for which you have registration authority with the searchForRegistrationDomains method. Example 6-6 shows how to set up a class to search for domains using these methods.

In Example 6-6, note the “Or” comment in the method beginDomainSearch. The comment appears to be relatively innocuous, but it brings up an important point about the capabilities of NSNetServiceBrowser. NSNetServiceBrowser may perform only one search at a time, and this holds true for domain and service searches. If you want to perform multiple searches, either wait for a search to stop and restart your desired search or create multiple instances of NSNetServiceBrowser.

Once the list of domains is obtained, use these strings to specify the domain you would like to search in for services. You also have the option of passing an empty string to indicate that you would like to search in the default domain, as was true when initializing an instance of NSNetService.