[ Team LiB ] |
6.3 Rendezvous Network ServicesRendezvous 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.
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. 6.3.1 NSNetServiceNSNetService 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. 6.3.1.1 Initializing NSNetService for publicationNSNetService 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. Example 6-5. Creating and publishing a network service// Socket used for listening for incoming connections bound to port 631 NSSocketPort *s = [[NSSocketPort alloc] initWithTCPPort:631]; NSNetService *serv = [[NSNetService alloc] initWithDomain:@"" type:@"_ipp._tcp" name:@"R&D Printer" port:631]; [serv setDelegate:delegateObject]; [serv publish]; // Remove the service from the network [serv stop]; 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. 6.3.1.2 NSNetService delegate methodsNSNetService declares that the delegates should implement the following methods:
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. 6.3.1.3 ErrorsNSNetService declares the delegate methods netService:didNotPublish: and netService:didNotResolve:. These methods notify the delegate of an error that may have occurred in a publish or resolve operation. Each method passes the net service object invoking the methods in the first argument, and a dictionary describing the nature of the error in the second argument. The error dictionary contains two keys: NSNetServicesErrorDomain and NSNetServicesErrorCode. The first key is for an object that shows where the error occurred: in the lower-level networking layer or in the NSNetService implementation. The NSNetServicesErrorCode key reflects the nature of the error by returning an NSNumber, which corresponds to one of the constants described in Table 6-1.
6.3.2 NSNetServiceBrowserThe 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. 6.3.2.1 Searching for domainsNSNetServiceBrowser 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. Example 6-6. Searching for domains using NSNetServiceBrowser// Assume the following instance method exists and has been initialized. NSMutableArray *domains; - (void)beginDomainSearch { // Assume browser is an instance variable browser = [[NSNetServiceBrowser alloc] init]; [browser searchForAllDomains]; // Or use [browser searchForRegistrationDomains]; } - (void)netServiceBrowser:(NSNetServiceBrowser *)browser didFindDomain:(NSString *)domainString moreComing:(BOOL)moreComing { [domains addObject:domainString]; if ( moreComing == NO ) [self updateUI]; } - (void)netServiceBrowser:(NSNetServiceBrowser *) browser didRemoveDomain:(NSString *)domainString moreComing:(BOOL)moreComing { [domains removeObject:domainString]; if ( moreComing == NO ) [self updateUI]; } 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. |
[ Team LiB ] |