Core OS • iOS, OS X • 56:46
Bonjour, also known as zero-configuration networking, enables automatic discovery of computers, devices, and services on IP networks. A wide range of Apple's products—from iMacs and MacBooks to AirPort Base Stations and Time Capsules, Apple TVs to iPhones, iPod touches and iPads—use Bonjour for streamlined and reliable networking. Learn how to use Bonjour to make it easy for your applications to publish, discover, and resolve network services.
Speakers: Stuart Cheshire, Rory McGuire
Unlisted on Apple Developer site
Downloads from Apple
Transcript
This transcript was generated using Whisper, it has known transcription errors. We are working on an improved version.
Welcome to WWDC. Before I start, let me ask for a show of hands. How many people are here in a Bonjour networking presentation for the first time? Wow, that's two-thirds of the room. It's amazing. It's great to see all the new faces here. What we're going to talk about today, I'm going to cover some of the history of how we got to here. This is the 10th year we've been doing a Bonjour networking presentation at the Apple Developer Conference.
For those of you who are new, I'm going to give some background about what Bonjour is, what it does for you, the platforms that are available for you to use it on, and the basic APIs it provides that you can use in your applications. And then we'll dive into more detail about the specifics of those APIs and how to use them.
So this is how we get to be here today at the 10th WWDC Bonjour presentation. Back in the 1970s, we had TCP/IP, and in the '80s, we had AppleTalk and NetWare, IPX, and NetBIOS on DOS and Windows machines. There were lots of different protocols all competing. But by the late '90s, it was clear that TCP/IP had won, and all of the other protocols were fading into obscurity. But protocols like AppleTalk had good ease-of-use properties that IP really didn't have at the time.
And towards the end of the '90s, there were some discussions on some mailing lists about transplanting some of the good features of things like AppleTalk onto IP. In 1999, the IETF formed the Zero Configuration Working Group, and a couple of years later, Apple announced that we would be building products using these zero-configuration technologies.
Very shortly after that, the first Bonjour printers started shipping because the printer makers had acutely felt the difficulties with ease of use on IP. Desktop publishing in the 80s and early 90s was one of Apple's strengths, and everybody knew you just plug a laser writer into the network, and you look in the chooser, and there it is, and you pick it, and you print.
But IP printers were much harder to set up. I know some people at a Silicon Valley startup. These were smart people with masters and PhDs in computer science doing a Silicon Valley startup, and they bought this fancy, top-of-the-line HP printer, double-sided, staple-in-the-corner, collating, thing the size of a refrigerator. Plug it into the network. The Mac users just pick it in the chooser, and they're printing.
After three days of struggling, they put it back in the box and returned it to Fry's because they couldn't get it to print from Windows or Linux over IP. Something was messed up with the settings. They pressed the reset button, which was supposed to turn it back to factory defaults. It didn't for some reason, and they just gave up.
And I told this story to some HP executives, and they kind of hung their heads and said, yes, we know. Stuart Cheshire, Rory McGuire So, the printer makers were really acutely aware that they were going to be killed with product returns and support costs if they couldn't make IP networking as easy to use as AppleTalk was.
Moving forwards in time, in 2005 we added Wide Area Bonjour to the Link Local Bonjour we had before that. When the iPhone launched, it had the same Bonjour APIs that Mac OS X did. In Mac OS 10.6, we added the Bonjour sleep proxy, which I will tell you a little bit more about. And that brings us to today, our 10th presentation.
So this is what the world looked like in 1990. At the physical layer, there were lots of different ways you could physically connect your computer to other devices. There were serial ports, there were parallel ports, there were networking protocols like Ethernet and Token Ring. So there was a lot of confusion. If you were making a product, it really wasn't clear what to use, or maybe you had to do all of them, but that was expensive. And at the software and protocol level, there were all these different protocols. Well, today, we all know the way things have gone.
TCP/IP is really the only credible networking protocol that you would think of using today. And if you're building a device like a laptop, it's pretty clear that what you put on it is Ethernet or Wi-Fi or both, but you're not going to put a token ring interface on.
And this is good for all of us because now when you show up in a hotel room, you know they're going to have an Ethernet jack, not a token ring jack in the hotel room. So standardization frees us from those silly standards battles and lets us get on with making really great products.
So let's dive down a bit deeper into how TCP/IP works. We've got different physical layers at the bottom. We've got the IP protocol layer, which is the waste of the hourglass. On top of that, we have transport protocols like TCP and UDP. And then running on top of TCP, we have connection-based protocols like web browsing, email, SSH. And on UDP, we have more real-time applications and packet-oriented communications like Skype and FaceTime.
An essential part of the IP stack is DHCP, which is what auto-configures your device with its IP address here at WWDC. That is also built on top of UDP. We have the domain name system for looking up addresses, translating names to addresses. That's also built on UDP. Well, this is great, and this is the state of the world as it was maybe about 10 years ago.
And if you plugged your laptop computer into a well-managed network, it would auto-configure and you'd get an address, and that's great. But with AppleTalk, you didn't need the administrator to set up the network for you. You could literally take two Macs and an Ethernet wire and plug them together, and you had a functioning network.
So we wanted that same bulletproof reliability on IP. So what we added in addition to DHCP was self-assigned link local addresses. And the way that works is that if your Mac or iPhone or iPad doesn't find a DHCP server, then instead of just giving up and failing, then it picks a random address in the range 169.254 and sends an ARP request. And if some other device answers, then you pick a different address and try again. Very simple, just like what AppleTalk did.
So, not really rocket science, not inventing anything new, but it brings that capability to IP. This has been around for a long time now. It was even in Mac OS 9 and Windows 98, so by now everybody should be familiar with that. And of course, IPv6 also has self-assigned link local addressing, too.
In the naming world, if you don't want to be typing IP addresses, you particularly don't want to be typing IPv6 addresses, you need to be able to look up names. And a similar situation applies. DNS is great if you have a DNS server set up and run properly. But for most home users, that's more hassle than they want. So we added multicast DNS.
And just like LinkLocal is not competing with DHCP, this is not competing with conventional DNS. Conventional DNS is still invaluable. But for communications on the same local link, when you don't have DNS, this gives you a way to use DNS names in your applications, in your user interface, in the normal way that the user expects. Except instead of sending the queries to a specific server, they're multicast on the network to all of your local peers.
And every device on the network runs a little piece of software called the multicast DNS responder. And when it sees a query for its own name, then it says, hey, that's me, and it sends back a standard DNS format reply using the standard syntax, standard packet format, standard names, and record types. Except the query came in over multicast. Now this is great. When we add these two things to IP, we've got ourselves to feature parity with traditional IPs.
And what I mean by that is if you know what name to type and you type it correctly and you don't make a mistake, then it will work. But if it doesn't work, you don't really know why or what to do about it. And Apple talk didn't make you type in the name of the printer. It let you pick it from a list in the chooser. So we wanted the same thing for IP. And that is DNS-based service discovery, which is a way of using standard DNS records, types PTR, SRV.
And TXT. And structuring those records in a way to facilitate service discovery. And that runs on top of the DNS infrastructure below. And by that I mean it can run on multicast DNS on the local link, or it can run using standard wide area unicast DNS queries. And that's what gives us wide area Bonjour. Thank you.
So here's the summary. These are the three legs of zero-configuration networking that you will want to build into your products if you're making a hardware product. You want link-local addressing, so you're not dependent on an external DHCP server. You want multicast DNS for name lookup, so you're not dependent on an external DNS server. And you want DNS service discovery, so that the user can discover your device instead of having to type the name in. This is all available as open source from Apple and from other independent implementations as well, so there's plenty to pick from here.
So what does that give us today? Well, lots of devices running Bonjour. You can discover printers and scanners. You can discover Apple devices like the Apple TV for AirPlay streaming or your AirPort Base Station for setting it up with AirPort Utility. You can discover network cameras. TiVos use Bonjour. If any of you in the room have a TiVo and you haven't got the TiVo remote app for the iPad, download it now. It is an absolutely wonderful way of managing your TiVo.
It's much, much better than doing it with the standard remote on the television screen. It uses the power of the multi-touch interface on the iPad and having a big touchscreen right in your lap to give you a much, much better user experience. it's done the normal up, down, left, right on a remote control. And of course, Macs and iPads and iPhones are also devices you can discover using Bonjour.
For you as developers, what platforms are supported? Well, obviously, Mac OS X machines have the Bonjour APIs, and Linux and Windows have the same Bonjour APIs. If you use Apple's open-source cross-platform MNNS responder code, there are also third-party implementations. And of course, mobile devices like iPhones and iPads and Android phones also support Bonjour.
Here are some of the ways that we have used Bonjour in Apple products. When I go to IETF meetings three times a year, they normally have a printer set up in the terminal room in case people need to print out things on paper. And next to the printer is a stack of printouts with instructions how to configure your Windows machine for printing.
And next to that is a single sheet of paper. They don't bother printing a stack. They just have one there. How to configure your Mac for printing? And the answer is, select Print on the File menu, and the ITF printer will already be there in the dialog box, and you select it, and you print. There is no step two.
In the Finder in Lion, we have two things that use Bonjour. We have finding local AFP servers on the network around you, which you already have seen in 10.5 and 10.6. Also new in Lion is AirDrop. In iTunes, iTunes uses Bonjour to discover shared music around you on the network, and it also uses Bonjour to discover AirPlay devices that you can play your media to.
In Safari, there's the Bonjour menu in the bookmarks list, which shows you a list of what it discovers on the network around you. If you have a network printer, they almost all have a web page for checking the ink levels and managing them, and you don't need to know the name to type in. You can discover that in Safari.
Safari also supports wide-area Bonjour, so it can discover things that are being advertised through conventional DNS that aren't on the local network. So if you haven't looked at this already, take a look in Safari and look in Bonjour, and you should see the WWDC developer information page appearing there. So if you forget the URL, it doesn't matter, because you can just find it in Bonjour and click on it.
Bonjour in iChat is great for events like this. There may be people around you in the room that you know by reputation. You've seen their postings on mailing lists, but you've never met them in person. They're not friends who are in your buddy list. Well, in the Bonjour buddy list, it shows you people who are around you on the same network, and it can be a great way to bump into people at conferences like this and chat with them and say hello and arrange to get lunch.
Time Machine, of course, uses Bonjour to find the Time Capsule, probably the easiest remote network backup system I've ever seen. It takes a couple of clicks and you're backing up over the network. And this is one of my favorites for a kind of perverse reason. The terminal, if you think about it, is emulating one of those old 110-board data dynamics teletypes from the 1960s, the things with the big roll of paper spilling out over the back as it prints.
And the terminal is emulating that. In a sense, it's the oldest DNA in OS X. And when the terminal engineers added Bonjour browsing to it, it really took me by surprise that even something so ancient can benefit from using Bonjour Service Discovery to find the SSH and Telnet and other services that are available on your local network.
So all of that is about ease of use. In 10.6, there was a new big feature of Bonjour, and that was the sleep proxy. Because we have all these new products that we offer. We offer BacksMyMac that lets you get to your machine at home and do screen sharing, but not if it's asleep.
We have USB printer sharing that lets you share your printer from your iMac, but not if it's asleep. We have Apple TV accessing your content off your iTunes music library, but not if it's asleep. So on the one hand, people want to put their machines to sleep to save power. On the other hand, we're offering all these great network services that encourage you to not put your machine to sleep. And we recognize the tension in this, so we produced the Bonjour sleep proxy.
And the way that works is when your machine goes to sleep, it finds a proxy on the network, transfers its Bonjour records to that proxy, because those records are a really nice compact description of the machine's role on the network. They give its name, its address, what ports it's listening on, what services it's offering, and it can ship that across the network.
The proxy then will then answer Bonjour queries on behalf of the sleeping machine, and when somebody tries to connect to it, the proxy will wake the machine up on demand. One of the things that's sometimes a little disappointing working in networking is that when you do the job right, no one notices.
When things are broken, they're quick to complain about it. But when it's working right, it's completely invisible if you do a good job. And this is one of those. After this shipped, several of my friends asked me, what did you do in the last year? And I said, I did the sleep proxy. And they said, what? And I described it.
And they said, that's really cool. How can I get it? And I said, well, you've already got it. And they didn't even notice. I notice because I developed this software originally on my old Quad G5 at home. My Mac Mini was the proxy and the Quad G5 was the client. And that thing takes so much power that when it wakes up from sleep, it makes the lights flicker. If I left that on 24 hours a day, it would cost me $1,000 a year in electricity to power that beast.
So when I'm sitting on the sofa on my laptop and I print something and I hit enter, as I hit enter, I hear the relays click on the G5 and the lights flicker. And every time it does that, I think, oh, that's cool. I invented that. Stuart Cheshire, Rory McGuire But for most of you, the iMac doesn't make any sound, its screen doesn't light up, it just wakes up, prints the job, and goes back to sleep. And you go and pick up your printout off the printer, and you give no thought to the minor miracle that just happened to make that possible.
And of course, we make wide use of Bonjour in iOS devices as well. We have it for AirPrint. We have it for AirPlay. We use Bonjour for home sharing to access your music. And the Apple TV remote control app uses Bonjour to discover the Apple TVs it's controlling. So that's great for Apple, but You guys are here because you make apps as well. So what about your app? Well, We're giving this presentation today because we don't want you to make apps that look like this, typing in addresses and port numbers and lots of manual configuration.
So, let's get down to the details. What do you do to add Bonjour to your application? The link local addressing is automatic. That's handled by the OS. Unless you're building a hardware device, you don't need to worry about that. The multicast DNS is automatic. That's handled by the OS. The area where Bonjour interacts with your apps is through the three APIs that we provide for the three basic Bonjour operations—Register, Browse, and Resolve.
Registering is when you have a device that's offering a service on the network, say Apple TV, which is offering AirPlay. When you power that on, it advertises its service on the network, and it sends out a couple of announcement packets just in case somebody was waiting for that Apple TV to come online.
A common misperception is that Bonjour works by continually broadcasting packets on the network, and it doesn't at all. We put a lot of work into making it very efficient. So on power on, the device will send a few packets to announce its presence, but after that it shuts up and it doesn't send any more. Now let's suppose you come into my house with your iPad, and you want to show me some pictures. So you want to browse to discover what AirPlay services are available in my house. House.
The iPad sends out a multicast query saying, "Who supports AirPlay?" And the Apple TV sees that and says, "Oh, I should answer that question." And each device on the network that can answer that question sends back its replies. And the iPad collects those replies, and you see a list of AirPlay services on the network.
Notice in the browsing operation, there were no IP addresses. It is just getting a list of names. And that's very intentional that we've made service discovery a two-step process, and Rory will talk more later about why that's important. The browsing is very lightweight, it just gets a list of names. When the user picks a device they want to use and taps on it, the second step happens, which is the iPad sends out another multicast query and says, okay, that's nice, I want to use your service now, tell me how to connect to it.
And the result of the resolve call is the IP address, the port number, additional text record information that some services need. And this is done at time of use. And that's very important, because in a dynamic world of DHCP and linked local addresses, IP addresses can change. Names are the stable identifiers, but IP addresses are not. So you save the resolve until connection time.
For those of you who are interested in the lower-level details of what's going on, I will explain how this is done with multicast DNS. And these are standard DNS queries that you can—you can do these to unicast DNS servers just using the DIG or the NSLOOKUP command on the command line. The first query in this example—let's suppose we're looking for things that implement IPP internet printing.
We send out a PTR query saying, "Who supports this service type, IPP.TCP?" And we get back a list of answers. And the first label of those answers is the rich text name that you display to the user. DNS names traditionally have been all lowercase digits, no punctuation, no spaces. And these are stored in the DNS, but they're not host names. They're not typed by users. So you're free to use whatever rich text you want, punctuation, spaces, even UTF-8 non-Roman characters.
Looking at that name, it's worth investigating a bit more. It's a structured name. It contains three parts. It contains the user-visible name, it contains the service type, which is a unique identifier for the type of operation that's being performed, and it contains the domain, which in this case is local, meaning use multicast DNS. But it could be anything else, like apple.com, which would mean use wide-area Bonjour and standard DNS queries.
When you developers produce applications that offer services or browse for services, choosing the service type is an important decision, so I want to go over that a little bit. A service type identifies two things. It identifies what the service does, for example printing, but it also has to identify how it does it. Because for a client to use a service, two things have got to match. The service has got to do what the client wants, but it's also got to do it with a protocol that the client knows how to speak.
Stuart Cheshire, Rory McGuire If I discover an IPP printer, but I only have an LPR client, that's not helpful at the software level. It may be helpful at a human level to say there's a printer there I can't use, but from the software perspective, the software wants to find the services it can talk to. Stuart Cheshire, Rory McGuire So I'll give a couple of examples. iTunes music sharing is basically HTTP GET.
But that doesn't mean it's a web browser. So if iTunes was browsing for HTTP, it would discover the configuration page for your printer, which is not very helpful. And Safari would discover your iTunes music collection, which is not very helpful because they're not web pages it can view.
Stuart Cheshire, Rory McGuire So the service type for music sharing is DAAP, Digital Audio Access Protocol. So even though it is syntactically looks like HTTP on the wire, it's semantically being used for a different purpose. So when you pick a service type name, bear that in mind. Stuart Cheshire, Rory McGuire And for development, you can use whatever string you like, but before you ship your product, please register it because that's how you ensure there are no conflicts.
Stuart Cheshire, Rory McGuire So imagine some Android developer who knows nothing about iTunes picks DAAP for their game. Well, now they're going to accidentally discover iTunes services on the network, which is not helpful. So it doesn't cost anything. It's very easy just to send in an email and register your unique service types.
And then the final step, what happens when we found those list of names when we want to connect? Well, those are two more DNS queries. There's an SRV query and a text query. And the SRV query gives us the port number, in this case, IPP services on port 631, and the host name is myprinter.local. The text record gives us additional key value pairs. In this case, it tells us that the printer supports the PostScript page description language.
And through the magic of DNS, the server included an additional record in the reply. We didn't ask for the address of myprinter.local, but it knows that we're probably going to need that next. So rather than make us do another round trip to fetch it, it adds that additional record in.
So that's how you can use Bonjour, and I want to show you a product that does it so you can see how this improves the ease of use. This is a network camera made by Axis. They're one of the biggest sellers of network cameras, and they're not cheap, so I guess that means people are willing to pay for quality. And of course, it supports Bonjour.
If you guys have ever tried setting up network cameras at home, you know that it can be—you better budget a Saturday afternoon for struggling with this thing to get it on the network. Well, with Bonjour, I'm going to run an app here called ZeroConf Spy, which will browse for Bonjour services. And we've discovered the camera, and it is telling Safari to open it.
[Transcript missing]
And if you've tried setting up a network camera yourself, you'll know it normally takes longer than that.
Some of you will have seen the Axis camera before. I have a new thing to show you this year. We don't have time to do a demo with this one as well, but I'm glad to see that there are more Bonjour-enabled network cameras coming onto the market. This is from a company called Stem.
They have a nice little iPhone and iPad app, so it becomes sort of an IP baby monitor. And that should be in the shops soon. I don't think they've announced the release date yet, but very soon. And with that, I would like to ask my colleague, Roary McGuire, to come up on stage and tell you more of some of the programming details.
Thank you, Stuart. So what are you all here for? You're all here to learn about all these cool APIs and how you can make your applications better, right? So that's what I'm going to go into. I'm going to go into the nitty-gritty details of what are the application programming interfaces that you guys can use.
What are the layers of the API? What are the actual calls that you make? And how do you actually get this to work? Now, in years past, we've actually only covered the Bonjour APIs. But this year, we're actually going to cover some of the socket APIs and things that you also need to use to actually get you up and running all the way from stem to start.
So first thing, Bonjour API architecture. There are five layers to the Bonjour API. The top two layers are your foundation and your CF layers, and these are the things that actually have the Bonjour calls in it: to register your services and advertise them on the network, to browse for services on the network that other devices are providing, and to resolve those services so that you can actually connect to them.
The DNSSD layer is the low Darwin Foundation layer. It's actually a C-layer API if you want to get down even closer to the underlying layers of Bonjour to do things in even more detail. Now, most of you will probably not need to go that low. Most of you will be just fine using the NSNet services and CFNet services, and that's what I'm going to go over. Again, like Stuart said, a lot of the stuff you don't have to worry about yourself. Link local addressing and host names and things like that, you won't have to worry about. That's taken care of by MDNS Responder and the kernel layer beneath.
So again, like I said, this year we're actually also going to go over how to do it from stem to stern, including all the sockets and things. So the two layers on top are the foundation and the CF layers that are going to enable you to actually use sockets, right? How do you actually get information from one device to the other after you've connected to the service? The Darwin Foundation layer for socket, bind, and listen is some of the stuff I'm going to go over so that you can actually implement a listening service on your device.
So instead of having just a Mac or something else that we don't really go into detail about, you have this listening service over here and you connect to it, this year we're actually going to go over how to create one of those, how to bind to a socket, how to listen on it, and how to register it with Bonjour so that your client can connect to it.
And again, the kernel's down beneath there, but you won't have to worry about that. So what do you do on the server side? First thing you have to do is get yourself a socket. Ask the kernel, "I need to file the script or to do some networking stuff." You're going to bind that socket to a particular address and a port, and you're actually going to ask the kernel for an arbitrary port.
Since Bonjour does the discovery of ports and host names and IP addresses, it doesn't really matter what port number you use for your service. So you can actually ask the kernel, say, "I don't care what port number I get. Just give me a listening port." And then once you get it, you actually tell the kernel, "I want to listen on that port." And then to make it even easier, once you've done all that kind of low-layer BSD socket stuff, I'm actually going to go over how to wrap it in a CF socket so that it's a lot easier to integrate with your run loops and your NS application main and all that stuff. Then the object that you're going to use for that wrapping is a CF socket ref.
Now, what are you going to do to actually advertise it on the network? This is the actual Bonjour call. The Bonjour API is NSNet service. Once you get the port number from the kernel that your service is listening on, you're going to pass that to NSNet service, and you're going to say, "Here's the name of my service, the type of my service, and the port number that you're actually listening on." I'll go over this in a little bit more detail. Then you actually tell it to publish, right? And that's actually going to advertise it on the network for your clients to see.
What about the client? So, over here on the client side, you're going to want to search for those services, the services of the type that you're using in development and then, as Stuart said, have registered once you ship, right? And you're going to search for those services that you can actually find. And this is the browse call. This is what you're actually going to use to do the browse.
Now, the browse call has a delegate, and the delegate method that's going to get called is did find service. When it finds your services out on the network, it's going to call you back with this thing. Now, keep in mind, it's going to call you back multiple times for each one of the clients that it sees on the network—or, sorry, each one of the servers that it sees on the network. If it sees only one service, you'll only get one of these callbacks.
If it sees multiple of them, you'll actually get several of them back. Now, the important part here that Stuart talked about a little bit is that you want your user to decide which one they want to connect to. Don't go connecting to every single service that comes back in did find service because it's very inefficient on the network.
[Transcript missing]
And now your client and your server are connected. Bytes that you write to the output stream on the client get sent to the input stream on the server, and from the output stream on the server to the input stream on the client. So what does this look like? Okay, back over here to the server side.
You're going to want to create a socket. That's the first line up there. AFInet means IPv4. Sockstream means you want TCP. And then you're going to create this socket adder object, and you're going to tell the kernel, "I want port number zero." Now, there isn't really a port number zero. This is the way you tell the kernel, "I don't care what port number you give me.
Just give me a port, and I'm going to use Bonjour to let the client figure out what the port is." Then you'll call bind and get sock name, right? Get sock name is actually the call that you make to figure out what port it was that the kernel gave you.
And you'll get it back out of the syn.syn_port. And then at the end you call listen, which actually tells the kernel, go ahead and start accepting TCP connections on this port. So, you'll also want to do this for IPv6. Now, as many of you probably know, yesterday was World IPv6 Day. It went extraordinarily well.
I think developer.facebook.com left IPv6 up. It went so well that there's not even news about it. It's not like the power grids went down and all Y2K stuff, right? So, you're going to want to do the same kind of thing on IPv6, but there's a gotcha here. You want to make sure that you use the port number that you got for v4. Bonjour does not make the distinction, really, between v4 and v6 because there's really only one host name for the device that's providing the service.
So, when your client resolves that service, they may get a v6 address back, they may get a v4 address back. So, you need to make sure you're listening on the same port number, otherwise your client may not actually connect. So, the part here that's important is that when you set the port number on the sock adder 6, you need to make sure that you set it to the port number that you got back from the kernel when you called get sock name.
So, now here is the glue to wrap this up in a CF socket. It's this thing called CF socket create with native. The kernel, the BSD layer APIs give you back a file descriptor, essentially just an integer. You can take that, wrap it in a CF socket object, create a run loop source, and put that source on your run loop.
Now, notice that we have specified this listening socket callback, and I will go over that in a couple of slides here. That's actually the thing that will get called back when you get an incoming connection, and I'll go over how to actually wrap the connection in some NS input stream and output stream.
So, this is hooking up the V4 socket. You have to do this twice—once for your V4 socket, once for your V6 socket, or you won't actually get your call back when somebody connects over V6. So make sure you also call this with your v6 socket. Now, for the slides, this is like copy-pasted code. It's probably a better idea to functionalize this and just pass in the file descriptor for v4 and then pass in the file descriptor for v6.
So, now to get to the actual Bonjour API. What do you do here to actually register this service? You have this thing from the kernel, and you've wrapped it in a CF socket, and it's listening and it's waiting for your client to connect. How do you actually advertise it over Bonjour? We can go all the way back up to the Cocoa Layer APIs, the Foundation Layer APIs, and you're going to create an NSNet service object and init it, and then schedule it in the run loop, set delegates so you can get callbacks, and then publish it.
So, the things to note here are, first, the domain. In Bonjour, right, we support local Bonjour on the link local network, and we also support wide area Bonjour over things like Back to My Mac. If you pass the empty string domain, MDNS Responder, the system service that takes care of Bonjour, will just pick all of the domains that it can, right? It will actually register them in all the domains, whether they're link local or Back to My Mac, if you have it, say, on your Mac.
Then the type. This is the service type that Stuart was speaking about. This is the type that you've registered, and this is how your clients will find your service on the network. The name is also interesting. If you pass in the empty string for name, it will actually, just like when the kernel picked an arbitrary port number for you, this one will automatically pick the pretty name of the device that you're running this on. For iOS devices, this is the name that you assign the device in iTunes. For Macs, this is the name that's in the sharing pref pane.
And of course you pass in the port. Now notice that we've wrapped this in end-to-HS, which is some low-level networking stuff because this API takes... Host order, host byte order. But the stuff that you get back in the SOC adder is actually network byte order. So if you don't do this and you can't figure out why you can't connect, it's probably because you weren't doing this.
And you schedule in the run loop, set your delegate, and call publish. What about the callback? Well, in order to figure out what the name was that MDNS Responder picked for you, you just call—sorry, you will get this callback for "did publish," and then you can get the name back out, and you can log it, you can display it in your UI, however you want to deal with that.
So this is the accept callback. This is the glue that when the CF socket stuff calls you back, this is how to wrap it in NS input stream and NS output stream. These slides are going to be available after the conference, and also there's a new sample coming out called Remote Currency. Didn't quite have time to get it done, but it's coming out soon, and it's going to have a lot of this stuff in it, and it'll be a lot better than just these slides can go into detail with.
So, what about the client side? Right, on the client side, we have to... Browse for services. Right, so how do you do that? Well, again, you use the NSNet Service Browser—sorry, not again. You use the NSNet Server Browser object, you init it, you set your delegate on it, and then you search for services of the type. Right, again, this is your registered type. And here, when you pass in the domain, again, it's going to try to search in all the domains that it can.
And here's the browse callback. It's going to do this "Did find service." Now, there's an interesting parameter in this callback that's going to be important for UI reasons. It's called "more coming." More coming is actually a hint that the underlying system daemon, MDNS responder, actually has more events for you.
So you don't necessarily want to update your UI when this flag is set, because you'll be spending a lot of time updating your UI. It's going to kind of flash in front of the user. Who wants bad user experience? Nobody. Use this as a hint to just change your model, and then when you get one of these back without the flag set, update your UI.
So, another callback that might happen is did remove service. If the application providing the service stops registering that service, MDS Responder will give you back one of these did remove services because Bonjour is dynamic. It knows when things come and go as long as the device has had enough time to say goodbye, I'm not here anymore, right? Now, if you crash or if your device falls off the Wi-Fi network, it won't have time to do that.
But you will get did remove service callbacks when it has time to do that, and later on, things actually time out, right? In Bonjour, things will actually time out if it hasn't been seen in a while, and then you'll get some more. You'll get a remove call. So, what about resolving? Again, if you're using the NS Layer APIs, you don't have to explicitly call resolve. All you have to do is get the input stream and the output stream, schedule them in your run loop, and call open. Once you've done that, asynchronously, it will resolve the service, it'll connect to it over TCP, and it'll give you a callback when it's done.
Again, the important thing here to make sure it's asynchronous is to schedule it in a run loop. If you call open before you schedule it in a run loop, you're going to block, you're going to get bitten by the watchdog, things are going to go bad. Okay, so those are the APIs from stem to stern. What about multitasking? So in iOS 4, we introduced multitasking. What does that mean for Bonjour services and networking in general? Well, let's go through a little timeline here.
Your application is going to get "application did enter background." It's going to tell you that the operating system is telling you, "Hey, the user switched off and you're now in the background." At this point, you should stop listening on your sockets and you should actually stop advertising, stop the register for Bonjour, because while your application is in the background, you won't necessarily be able to accept new incoming connections.
Now, there are a lot of reasons that we did that the operating system actually behaves this way, but the biggest reason is power, right? If you're constantly listening and constantly doing network stuff, even when the user can't interact in this application, then it's just going to be bad for battery life, right? So battery performance reasons, stuff like that. So notice that sometime after "application did enter background," not necessarily immediately after you return from that delegate callback, your code will stop running. You can't really tell when that is because your code is not running.
Now, you can request a task completion, and there's some good technical publications up that talk about different ways to do things in the background. You can request a task completion if you're, say, downloading something small. You can say, "Okay, well, I need to run just a little bit longer," and it'll extend you out just a little bit. But again, it's not going to be forever, right? You're going to, at some point, stop running.
Now here's the interesting part here. After your code stops running—and again, you can't tell this is happening because your code is not running—your Bonjour operations may get canceled. The inter-process communication from your application down to the system service is going to get severed for battery-in performance reasons. The other thing that might happen is that your connections are going to get closed. That's different than socket close, right? When your code is not running, you did not call close on the socket.
But the kernel is going to send TCP fins to your peers. Your listening ports will stop listening, and the kernel will reclaim the resources that go along with your sockets. So when your code actually starts running again, you need to make sure to deal with the aftermath of that stuff. Now, if your user has actually just gone to another application and come back really quick, this may not have happened.
So you don't necessarily want to stop your Bonjour browsers and stop your outbound connections when you get application did in the background. You are going to want to do it for listening sockets, and I have a note on Tech Note that's really greatly written, and we'll give you a lot of details on that information.
[Transcript missing]
So, what will actually happen in the aftermath? Right? This is the browser callback that will happen. Did not search. Now, this callback also happens if there was an error when you first started to browse, but when you come out of the background, this is what you're going to get.
This is when you're going to have to say, "Okay, let me make sure. Does the user really want to browse again? If so, let's restart the browse and update the UI." Keep in mind, you will not get remove events for all of the stuff that you had previously gotten add events for. This thing tells you, flush your cache of the events that you had gotten. They're gone. If you start again, you will get add events for all the stuff that's on the network, just as if you started again, because you did start again.
So two years ago we introduced Bonjour peer to peer over Bluetooth in iOS. And we have now in iOS 5 made it opt in. There were some performance things and some networking things that now made this opt in. This is only if you link against the iOS 5 SDK. If you link against something earlier than that, you're still going to get the old behavior.
So it's not like if your users update to iOS 5, they're going to get some new behavior. If they update to iOS 5 and still have your old app that was built against something before 5, then they'll still get the old behavior. And the flag is exposed in the DNS layer API, DNS SD layer API.
Okay, so now let's take a step back and go to tips and reminders. Again, I cannot stress this one enough. Use asynchronous calls. For example, the DNS timeout is 30 seconds. DNS has been around a long time, and the 30-second timeout has worked really well in practice. iPhones have not been around as long, but we've determined that the watchdog timer for pulling events off of the event manager in your main thread, about 20 seconds. So, if you make a blocking DNS call on the main thread and your DNS server just doesn't respond to you, you're going to get ejected. Right? The watchdog is going to bite you.
And your app dies, and your user has no idea why. It looks like you crashed, but you didn't. So, use the NS layer APIs, NS operation, and stuff like Grand Central Dispatch. There were some great sessions on Grand Central Dispatch this year. Unfortunately, they're all before this one. As a matter of fact, right before this one in this room. So, go back in time and go watch that one. So, browse call. The right way to do the browse call.
Don't have a refresh button. Bonjour is dynamic. You will get add events, you will get remove events. There's no reason for you to refresh. MD&S Responder will handle that stuff for you. Don't do open-ended browsing. If you're not displaying a browse UI to your users, they probably don't care about browsing, and you're going to be using network resources that you don't have to be using because your user doesn't care about it right now. Also, stop browsing when you're not displaying that UI anymore. Don't start browsing when you first display the UI and then keep the browse going forever and ever and ever, particularly because when you go into the background and come back out, that browse may be dead.
So for your Mac developers, a little suggestion here: use Windows, not pull-down menus. Right? If you're mousing over a menu and all of a sudden it changes out from underneath you kind of right before you click, bad user experience. Users are generally more comfortable with menus being static. Now, the next thing is also very important for networking in general.
Resolve and connect only when your user has selected the service. Don't connect to every single thing that you find in the browse. It uses the network unduly. So here's a little graphic that we put together. So as Stuart said before, when you browse, you get a little bit of information about every service. You get the name of all the services of the type that you're looking for. When you resolve, you get a lot of information about one service. Right? Do not go fill out the entire table. It's bad for the network and bad for your user experience.
So, saving services you find. Bad ideas for saving the information that you get back from resolving. Don't just save the IP address. IP addresses change, particularly link local addresses and addresses in IPv6. In Lion, we have temporary private addresses for v6, and those things may change—or those things will change—about every 24 hours. If your device goes off the network and comes back on, it might have changed its address just because the DHCP server decided to give it a new one.
Don't save just the IP address. Also, don't save the port number. If you follow the direction here and you ask the kernel for port number zero, the next time you ask the kernel for port number zero, you're probably going to get a different port number. So, on the client side, don't save the port number because you probably won't be able to connect to it.
As a matter of fact, don't even save the host name. The host name can actually change. If the user goes and, say, you know, changes the name of their machine, that's not necessarily what you want to save. The right way to do it is late binding, right? The service is defined by the tuple of name, type, type in domain, that's what you want to save.
Again, service naming and registering. There's a unique identifier for each service type, 15 characters maximum, letters, digits, and hyphens are allowed. Go to this URL and register your type. It's real easy. It's free. More information. We've got some great evangelists that help us out with the Bonjour stuff, and we have some great documentation and sample code. Again, there's a new sample coming out in a couple of weeks called "Remote Currency" that you should all look at.
There's the book written by my colleague Stuart, and of course the Apple Developer Forum. So for the third of you that have been to Bonjour before, the Bonjour stuff before, and know a little bit more about Bonjour, you can help out those that are new and that don't know as much. Some related sessions that are all in the past.
If you don't take anything else, these are the things that you should remember. Bonjour makes networking easy and reliable. It makes things really easy to do, and Apple uses it all over the system, and we make these APIs public, and you guys can use it as well. It's also public for things like Linux and Windows, Android phones, that kind of stuff, so it's cross-platform.
Use asynchronous calls across all of networking, not just Bonjour. Do not get bitten by the watchdog. Let the user decide when they've waited long enough. Don't just say, "Well, I haven't been able to connect for 10 seconds. I'm going to stop." Ten seconds might be great for one network and horrible for another network. Let the user decide when they're done waiting for stuff.
Use late binding. If you're going to resolve a service once and then connect to it multiple times, say, like, when you choose a printer, you want to keep the name type in the domain, not the port number, not the IP address, not even the host name. the service name, the service type, and the domain in which it was discovered. Handle errors when you get back out of the background, because you may encounter them depending on when the kernel needed to reclaim resources. And register your service types. Thank you very much.