Editor's note: In this first part of a short multi-part series, Mike Beam discusses some of the thinking behind Rendezvous (ZeroConf) and the hooks in Cocoa to implement its functionality into your application design. In next week's part two, he shows you how to build a simple chat program that attempts to provide functionality similar to iChat's Rendezvous chat feature.
As Cocoa developers, what do we need to know about Rendezvous? And how can we effectively use it in our applications? These are questions that I want to answer in today's column.
Rendezvous is so much bigger than many of us initially imagined. At least when I first heard about it, I wasn't as impressed as I am now. Apple is pushing this technology with everything they've got. They want -- indeed, they need -- developers on all platforms and manufacturers of all sorts of peripherals to look at Rendezvous and realize that this is something that will really make customers think "Wow, that is so cool." And it really is that cool.
As an example, consider what Derrick Story, the managing editor of our little corner of the Internet, has written about previously: how the folks in the O'Reilly offices have started using iChat as a tool for communicating with each other, quickly and efficiently. Communicating with one another with a chat program may not sound like much, as we can do that today without Rendezvous using any number of chat clients.
However, consider that iChat works simply by turning the power on. You don't need to set up IP addresses and host names, and there's no need to pass around your IM screename or collect those of your colleagues. It just works. You log into Rendezvous chat and immediately you see and are seen by everyone on the network running iChat.
Couple this with AirPort and the proliferation of laptops, and you get something really cool. Those of you who were at the Mac OS X conference got to see this in action on a large scale. Everyone had their laptops out, and you could simply open up iChat as described and see your buddy list instantly populated by all of these people around you and in other rooms. I haven't attended many tech conferences, but the impression I got from those who I talked to about it was that this sort of thing was unprecedented.
Not surprisingly, the idea of Rendezvous is as old as networking itself, and it has indeed existed in various forms over the years. AppleTalk is the most successful technology that provided a zero-configuration networking-like experience. But AppleTalk is built on proprietary technology, which must be supported by interested parties in addition to standard IP networking. And there were others, which never made a big splash. Basing ZeroConf on IP eliminates much of the work developers and peripheral manufacturers need to do to get it working with their product.
Rendezvous is platform-agnostic, which is a huge benefit to application developers like ourselves. Apple's open source implementation of Rendezvous is a relatively small collection of code, roughly 5000 lines of portable ANSI C. In this article, we're interested only in the Cocoa API to Rendezvous, but keep in mind while we work through this article that everything we do can be implemented on other platforms and made to interoperate with Cocoa implementations seamlessly.
In other words, I can publish a service using Cocoa's
NSNetService class, and pick up that service on a Windows or Linux machine with an application using the mDNS responder C API. This may seem obvious, but its important to realize that Rendezvous has no predisposition for Mac OS X; it doesn't care about your platform.
For us Cocoa developers, adding support for Rendezvous is a no-brainer. If your app gets put on the network, then it needs to advertise whatever it does using Rendezvous. Getting your app to work with Rendezvous is easy and painless. The Cocoa APIs for Rendezvous exist in the Foundation classes
NSNetServiceBrowser, and after spending a little time working with these classes, you will see how easy this stuff is. Before we get into the Cocoa, let's spend some more time discussing what was needed to make ZeroConf networking a reality.
When we read up on the ZeroConf Web site, or almost anywhere else describing ZeroConf, we're presented with a set of problems that face users in their quest to connect computers to a network and have them communicate with each other usefully. When you want to do something on a network, the normal course of action is to somehow obtain an IP address for your computer. This is often accomplished by manually entering an IP address, which might be assigned to you at work by your systems administrator, or it might be one that you know will work on your home network. DHCP servers were designed to automatically hand out IP addresses to hosts, but you may not have one available, or the one you usually use may be down for the count.
Once you have an IP address and are connected to the network, users often have another problem: they have to keep track of, and remember, obscure numbers for the various hosts they use. People like to refer to things by name, not by numbers. So we assign to computers hostnames that we can type in to connect to them. But for this to work properly you need to either have a list of hosts and their addresses on your computer, so applications can translate between names and numbers -- because computers only like to work with numbers -- or you might have a domain name server somewhere on your network to do that job.
Raise your hand if you're running a DNS server on your home network. Not many people do. Moreover, even if you had a DNS server to translate hostnames into addresses, you may not have control over changing your hostname. People would rather be able to set their computer's name from their computer, and not have to track down the guy in charge to do it for them.
So you have a hostname, you're on the network, people know how to connect to you, you know how to connect to other people, and everyone is happy. Right? Well, you will want to make use of services that other people on the network are running from their computers, such as printing, file sharing, instant messaging, and so on, and people may want to make use of services you offer.
But to do that, you have to know about the existence of said services beforehand, along with the IP address of the host machine, or host name if they have one, and you have to communicate this information back and forth to people. Large, well-organized networks steer clear of this confusion by setting up directory servers that keep track of what services are offered where. For small, ad hoc networks though, you won't likely have a directory server.
ZeroConf tears down these barriers to networking for normal people by providing solutions the following three needs:
The Reader's Digest version of the above is that ZeroConf networking provides solutions to addressing, naming, and service discovery that conspire to make IP networking as easy-to-use as AppleTalk networking. We'll briefly address (pun intended) each of these areas in turn.
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.
... 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.
Now, lets get on to the Cocoa and see how it works for us.
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:
NSNetService's publication APIs let you create and define a service and publish it for Rendezvous browsers to find.
NSNetServiceBrowserprovides the APIs for developers to make this possible in their application.
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.
Copyright © 2009 O'Reilly Media, Inc.