DekGenius.com
[ Team LiB ] Previous Section Next Section

6.3 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.

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.

6.3.1 NSNetService

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:

  1. Configure a listening server socket that clients will connect to.

  2. Initialize an NSNetService object with the service domain, type, name and port.

  3. Assign a delegate object to this instance.

  4. Handle any messages received by the delegate.

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 publication

NSNetService has two initializers:

  • initWithDomain:type:name:port:

  • initWithDomain:

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 Internet Assigned Numbers Authority maintains a catalog of service names and a list of ports where applications can find these services. You can find this catalog at http://www.iana.org/assignments/port-numbers. The /etc/services file contains a similar catalog of service names and port numbers.

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 methods

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.

6.3.1.3 Errors

NSNetService 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.

Table 6-1. Net services error codes

Error code

Description

NSNetServicesUnknownError

An unknown error occurred.

NSNetServicesCollisionError

This error results when a service tries to register a service under a name that is already taken.

NSNetServicesNotFoundError

The service (attempting to be resolved) could not be found on the network.

NSNetServicesActivityInProgress

The net service is busy and cannot process the request.

NSNetServicesBadArgumentError

An invalid argument was used when initializing the NSNetService instance.

NSNetServicesCancelledError

The client cancelled the action.

NSNetServicesInvalidError

The net service was configured improperly.

6.3.2 NSNetServiceBrowser

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:

netServiceBrowser:didFindDomain:moreComing:

Notifies the delegate that a domain was discovered.

netServiceBrowser:didRemoveDomain:moreComing:

Notifies the delegate when a previously discovered domain becomes unavailable.

netServiceBrowser:didFindService:moreComing:

Notifies the delegate that a service was discovered.

netServiceBrowser:didRemoveService:moreComing:

Notifies the delegate objects that a previously discovered service was removed from the network while searching.

netServiceBrowser:didNotSearch:

If an error occurs, this method notifies the delegate. The first argument is the service browser instance reporting the error, and the second argument is the error dictionary that contains information about the nature of the error.

netServiceBrowserWillSearch:

Notifies the delegate that the network is ready and the search is about to commence. The method is passed the service browser instance that is about to begin searching.

netServiceBrowserDidStopSearch:

Notifies the delegate that a search ended as a result of a stop message to the service browser object. It can perform housekeeping tasks when the search completes.

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 domains

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.

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 ] Previous Section Next Section