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.
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:
Configure a listening server socket that clients will connect to.
Initialize an NSNetService
object with the service
domain, type, name and port.
Assign a delegate object to this instance.
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.
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.
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.
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
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.
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
.