Incorporating Rendezvous into Your Cocoa Applications, Part 1
Pages: 1, 2
The first thing ZeroConf had to tackle was how to get an IP address for a host in the absence of other means of obtaining one. For example, if a computer is set up to get an IP address from a DHCP server, how should it get one if the DHCP server doesn't exist? Manually setting your IP address won't do, because that's configuration, and it would require a certain degree of coordination with peers to get hosts to talk to each other on the same network. ZeroConf's solution to this problem is quite simple, and it had in fact been solved already.
The solution is with automatic address allocation in the link-local range. A link-local address is an address in the range 169.254/16. What this means is that the address is something like 169.254.30.23. Link-local addressing is already present in most operating systems. Windows 98 was the first to introduce it, Mac OS 8.5 soon adopted it, and it is present in Mac OS X. In fact, the future of addressing standards for the internet, IPv6, has this feature built-in.
The way link-local addressing solves the problem of obtaining an IP address is quite simple. A host, with no static IP address and in the absence of a DHCP server, will simply pick an address in this link-local range. The host will then put this address out on the network to see if it conflicts with existing IP addresses. If it is found to be in use by another device, the host will simply pick another address in the same range, put it out the network, and continue until an unused IP address is found.
You can tell if your computer is using the link-local address in Network Preferences if the address starts with 169.254, which will be further annotated with the label "self-assigned", as shown here:
Note that if a host can obtain an IP address by normal means, Rendezvous will use that address without a hitch. Additionally, because Mac OS X supports multihoming (which means that a host can have multiple IP addresses for a single interface) users who are connected to a service through a link-local address will not be disconnected in the event that the host obtains a "real", Internet-visible, IP address.
However, as we mentioned earlier, IP addresses are hard to remember, and humans prefer to work with names instead of numbers.
The Internet is built on the Domain Name Service (DNS). Without DNS, names don't get translated into IP addresses. This poses a problem, because people remember and work with names, not numbers. Computers, however, work best with logically-formatted IP addresses. DNS exists to translate between the two.
Unfortunately, on small, ad hoc networks such as those that exist in conferences, homes, small offices, coffee shops, and airplanes (old school Apple commercial there!), there are no DNS servers to translate names to addresses, and we users must type in an IP address to connect to a host. Stuart Cheshire sums it up best in his IETF draft proposal for multicast DNS. He says:
It is easy for those of us in the IETF community who run our own name servers at home to forget that the majority of computer users do not run their own name server and have no easy way to create their own host names. When these users wish to transfer files between two laptop computers, they are frequently reduced to typing in dotted-decimal IP addresses because they simply have no other way for one host to refer to the other by name. This is a sorry state of affairs. (draft-cheshire-dnsext-multicastdns-00.txt)
Multicast DNS (mDNS) is ZeroConf's proposed solution to this naming quandary. mDNS is a system whereby hosts each run their own mDNS responder. The mDNS responder is a way of providing traditional domain name services where each host can respond to name queries that they how to answer. Traditionally, hosts contact a known DNS server for name lookups. With multicast DNS, when a host needs to look up a name, it sends the query out to the local multicast group that contains all of the hosts that have registered Rendezvous services on that domain. Think of it as the democratization of domain name services: rather than hosts relying on a central server for name lookups, hosts rely on any other host running a multicast DNS responder.
When a user types a name in to a Web browser, that name is translated to an IP address by a DNS server somewhere. These names are in the form of www.apple.com, www.oreilly.com, and so forth. When a user enters an address in the form
hostname.local, the name lookup is performed using mDNS. That is, the client sends out to the network a request that is received by all hosts running an mDNS responder. If a host has the information being requested, it will send a response. The responder on the specified server indicated in the name then sends back its address, which the client uses to connect to the server. Its an elegant and simple solution to the problem that changes very little about how people know DNS to work.
DNS multicasters will keep track of responses to all queries, even if they haven't requested the information being passed around. The idea is that by keeping track of questions and answers about Rendezvous services on the network, if some query a multicaster needs to perform has been handled already, it will know about that and not ask the same question again. This helps cut down on network chattiness, for which AppleTalk is infamous. This is just one way that chattiness is cut down with ZeroConf; there are more that we will not cover here, but are discussed extensively in many of the resources referenced at the end of this article.
Note that I said that mDNS is used for hostnames ending in
.local. Mac OS X users can pick their own Rendezvous names that other people can use to connect to their machine in the Sharing preferences pane, as shown here:
In a nutshell, that's ZeroConf name and address translation. As you can see, both of the naming issues have been resolved: users can pick any name they want (as long as it follows the rules for host names), and other users connect to hosts without resorting to typing in dotted IP addresses.
Service discovery is the heart of Rendezvous, and automatic naming and address allocation are in fact means to this end. The Rendezvous APIs that we have to work with exist for applications to advertise themselves over a network as a service, and for applications to search on a network and discover services of a specified type. When an application discovers services, it should present those services to the user in a UI from which the user can select a service to which to connect. In our application, the service browser UI is nothing more than a simple
Rendezvous uses a multicast DNS responder to advertise and search for services. The details of how DNS is used for this are beyond the scope of this article, but I recommend to anyone who is interested to read the documents that will be listed below.
Straight From The Horse's Mouth ...
... so to speak. ZeroConf networking is a work in progress. Currently, ZeroConf has an Internet Engineering Task Force (IETF) working group that is committed to hammering out the details of ZeroConf. Their Web site can be found at www.zeroconf.org. The following documents come from the ZeroConf Web site and discuss the various challenges and problems the working group had to solve, as well as the solutions they came up with.
- Dynamic Configuration of IPv4 Link-Local Addresses
- Requirements for the Replacement of AppleTalk Name Binding Protocol
- Performing DNS queries via IP Multicast
- Discovering Named Instances of Abstract Services using DNS
Now, lets get on to the Cocoa and see how it works for us.
And Now, The Cocoa
What we've discussed thus far are the concepts behind the nuts-and-bolts of ZeroConf networking. What we're really interested in today is adding support for Rendezvous within your network-based application. In Mac OS X 10.2 and later (if you're still on 10.1.x, I apologize -- Rendezvous is only available on 10.2 and later) we have several Rendezvous APIs to choose from when building our applications, including the DNSServiceDiscovery C API and the Core Foundation APIs found in CFNetServices. What we're interested in, however, are the APIs found in
NSNetServiceBrowser, both of which are a part of the Foundation framework.
Instances and Instances
In object-oriented programming, we talk about instances of a class all the time. Objects are instances of a class. In this article, I will frequently be using phrases like "instances of a network service" or "an instance of a service." These phrases are not referring to objects that are instances of
NSNetServiceBrowser provide three key areas of functionality:
- Service Publication
Your application provides a service to other applications over a network, and
NSNetService's publication APIs let you create and define a service and publish it for Rendezvous browsers to find.
- Service Discovery and Browsing
One of the basic requirements for Rendezvous is that users should be able to browse for instanscces of a particular network service.
NSNetServiceBrowserprovides the APIs for developers to make this possible in their application.
- Service Discovery and Browsing
Once a service has been discovered, a client must resolve the network address and port number of the service so it can connect to it.
NSNetServiceprovides a resolution API that lets us resolve discovered services, and determine connection information for that service.
So the focus of the remainder of this article are the net services APIs that exist in
As an aside, I find it telling that the Rendezvous classes are a part of the Foundation framework. If nothing else, this gives some indication of how central a position Rendezvous is taking in Apple's future development, and the position Apple would like for us to give Rendezvous in our applications. What's more is that in Mac OS X 10.2 the networking classes in the Foundation framework have, with little fanfare, been beefed up to provide general support for networking with BSD sockets (sockets are the de facto standard for sending data across a network, and they will be discussed only in passing today, and in much greater detail in the next article).
The two most notable examples of this are in
NSSocketPort was used exclusively for setting up distributed object connections between networked hosts. Now, we have several methods that let us create general-purpose sockets for networking.
NSFileHandle, while supporting reading and writing to file descriptors, had no support for asynchronous background communications over a socket, which is no longer the case. We'll talk a lot more about these things in a future article.
Before we dive into the Rendezvous classes, we must first review an Objective-C design pattern that is central to programming with these classes: Delegation.
Delegation is a design pattern in which one object (the delegate) acts on behalf of another object. The owner-delegate relationship is generally one that we explicitly create. That is, in classes that use delegate objects, we have to tell instances of that class what their delegates are. In Cocoa, this is usually done with the method
In the Rendezvous classes, delegation is used to achieve asynchronous operation of a process. What this does for us is let some long process do its thing in the background without leaving the user hanging with a frozen user interface. We all know how much we hate it when our interface locks up.
It works like this: ordinarily, when we invoke a method, the method will churn away doing its thing on its own time, and return with a value. In a single-threaded application, the flow of execution in an application is stopped while these methods work. This is known as synchronous operation, and is usually not a problem, since most methods do their thing really fast. However, communicating over a network can be excruciatingly slow, and when network operations are performed synchronously in a single-threaded application, the thread of execution -- which includes making the UI respond to events -- is stopped dead in its tracks while a request is sent to a server and processed, and a response is sent back to the client. This can be fast, but it can also be slow, and even really slow. This is no good.
To solve this problem
NSNetServiceBrowser sidestep the issue of network latency through delegation. In this system, when you invoke a method, for example the method to search for services in
NSNetServiceBrowser, the method returns immediately and has no return value. Since the method returns immediately, the main thread can go on about its very important business of keeping the UI responsive.
Meanwhile, a new thread is launched to listen for a response from the network. When something does happen, the delegate is notified and the user interface is updated accordingly. In the case of publishing a service, the delegate is notified just before the service is published (i.e. "The network is ready, so I'm going to go ahead and publish the service."), and also if an error occurs. The exact methods
NSNetServiceBrowser delegates respond to will be discussed in part two of this series.
In the next article, we'll begin building a simple chat program that attempts to provide functionality similar to iChat's Rendezvous chat feature. We'll actually spend two columns working on this: first building the Rendezvous service publisher and browser, and then giving the application the ability to talk to other instances of the application on the network. Don't miss it!
If you want more stuff to play with while we're working through this series of tutorials, I suggest downloading the open source Rendezvous package from Apple. This download contains source code that will compile and run on the major platforms used today, including Windows, Linux, and any Posix platform. Specifically, this code allows you to embed a multicast DNS responder in their non-Mac OS X application. In Mac OS X, the multicast DNS responder is a system service, so there's no need to implement it on a per-application basis.
With the Developer Tools installation there are several example applications that show how to use Rendezvous. One pair of applications is a Picture Sharing service and a matching Picture Sharing browser. These two applications are based on the Foundation classes that we discussed in this column and are found in /Developer/Examples/Foundation.
If you'd like an example of how to use Rendezvous from the Core Foundation check out the example code found in /Developer/Examples/Networking/Echo and /Developer/Examples/Networking/Echo Client. These examples show how you can use Rendezvous in a command line tool.
In another effort to spread knowledge about Rendezvous, the Apple Developer Connection has made available on its Web site the Rendezvous videos from the last WWDC. Stuart Cheshire's WWDC presentation on Rendezvous is very informative and gives a broad overview of the problems they had to solve and the solutions they came up with. You must have an ADC membership to view these, which can be had for free. There is a link to these videos and more from Apple's Rendezvous developer website which is a great repository of information about rendezvous.
Michael Beam is a software engineer in the energy industry specializing in seismic application development on Linux with C++ and Qt. He lives in Houston, Texas with his wife and son.
Read more Programming With Cocoa columns.
Return to the Mac DevCenter.