Configure player

Close

WWDC Index does not host video files

If you have access to video files, you can configure a URL pattern to be used in a video player.

URL pattern

preview

Use any of these variables in your URL pattern, the pattern is stored in your browsers' local storage.

$id
ID of session: wwdc2002-805
$eventId
ID of event: wwdc2002
$eventContentId
ID of session without event part: 805
$eventShortId
Shortened ID of event: wwdc02
$year
Year of session: 2002
$extension
Extension of original filename: mov
$filenameAlmostEvery
Filename from "(Almost) Every..." gist: ...

WWDC02 • Session 805

Introducing CFNetwork

Networking and Server • 1:01:02

CFNetwork provides APIs to help you communicate with the standard services on the web, as well as ways to manage your own. This session explains CFNetwork and how it fits into Mac OS X, including power-user HTTP communication, detecting or broadcasting network services, and configuring socket streams to navigate a firewall.

Speakers: Becky Willrich, Jeremy Wyld

Unlisted on Apple Developer site

Transcript

This transcript was generated using Whisper, it has known transcription errors. We are working on an improved version.

It's a real pleasure to be here today to introduce for the first time the CFNetwork APIs at WWDC. Okay, so what we're going to start with is just a basic overview of what CF network is and what it's intended to do. Then we're going to back up for a second and talk about some basic concepts that CF network inherits from Core Foundation itself. Once we've covered that, we're going to get into the meat of what the APIs actually provide, and we're going to start with CF socket stream. Then move on to the HTTP engine inside CF network.

There are two types associated with that, CF HTTP message and CF HTTP stream. Then we're going to talk about CF net services. Those are the APIs that provide you with access to rendezvous and all of its power. Then finally, we're going to wrap up with a simple demo about how it all comes together.

So I'm going to start out with the overview. And we're going to be answering these five questions. What is CFNetwork? How does it fit into the Mac OS X stack as a whole? What does it provide to you? When you should be using CFNetwork, as opposed to one of the other APIs available on Mac OS X? And finally, where is it available?

So, CFNetwork provides three basic chunks of functionality in Jaguar. It provides, basically, libraries of abstractions for network protocols you all know and are familiar with. So that means sockets, HTTP, and the new rendezvous APIs. The APIs are all in the style of core foundation. We export all of our functionality using CF types, and we build on all of the the types available in core foundation, most notably the run loop.

So what are the goals of CFNetwork as a whole? The primary goal of CFNetwork is to expose the full power and functionality of the underlying protocol. We try to make the protocol accessible to you in easy and natural terms, but we do it without hiding any of the power, any of the flexibility of whatever protocol lies underneath. We try to have strong integration with the existing API paradigms on the system. Most notably, again, is the CFRunLoop, which, whether you're a Carbon app or a Cocoa application, brings the user events into your application for your processing.

We make sure that our overhead compared to raw BSD sockets is little to nothing. There is a slight memory footprint overhead, and as far as throughput goes, there's almost no overhead. It's in the noise, provided the packet leaves the computer over the network. However, what we will not do is we will not protect you from the details of the protocol. We can't provide you with the full flexibility of the protocol while shielding you from those details.

So how does it fit in? You've probably seen this diagram or something pretty similar a few million times by now. It's the basic API stacks available to you as a developer. And here I've overlaid the basic networking APIs. Down at the Core OS layer in Darwin, we have Berkeley Sockets.

That's our foundation. Everything inside CFNetwork is done on top of TCP/IP. CFNetwork itself resides inside the core services layer, so it's beneath all the application frameworks. That's also below the UI layer, so it's accessible to you if you're a faceless application or a daemon. And then up inside the application frameworks, Carbon and Cocoa, you see some of the other networking APIs on the platform. They're built on this foundation of CFNetwork on top of Berkeley Sockets.

But it gets more interesting. If you look inside of core services itself, you can find some more layering. And here we can see that CFNetwork is built on top of the foundation provided by core foundation. In particular, we use CFURL, CFStream, and CFRunLoop quite heavily. And then other core services networking APIs are built on top of CFNetwork. So the two most notable examples of that are the new web services framework, which provides access to SOAP and XMLRPC messaging, and NSL.

So just to summarize, CFNetwork, part of the core services layer, builds on top of BSD sockets plus core foundation, used by all of the higher-level frameworks like NSURL, NSL, web services. But it's also used directly by many apps on our platform, largely at times when they want a performance boost relative to using the more convenient higher-level APIs. So just as some examples, Mail, Software Update, Sherlock, iPhoto, and the new iChat all use CFNetwork for their networking needs.

Okay, so what do you find inside CFNetwork? Three basic pieces, some common socket abstractions that provide the basic streaming model that we're going to use throughout. An HTTP 1.1 engine, so that's fully persistent, pipelined connections, exported through two types, HTTP message and HTTP stream. And finally, the Net Services layer, which provides API access to zero configuration or rendezvous. And we have two types there, CFNet Service and CFNet Service Browser. And we're going to walk you through all of those APIs in detail here.

So, finally, when should you use CF network? Well, use CF network precisely when you need a low-level, high-performance framework. If you're thinking about, should I use CF network instead of one of the higher-level APIs, you should drop to CF network if this describes you. You want to work directly with the socket more intimately.

You need exacting control of when the bytes are read or written to the network. You're not happy with letting a higher-level framework move the bytes for you. You need detailed control of HTTP and an HTTP transaction. You want to link low. You want to avoid linking with something in the UI layer.

or you need new features that are only available in CFNetwork, and Net Services would be the big example of that. But please, by all means, use the higher-level APIs when it's possible. They're more convenient. They're simpler. They just don't give you as much power, so you have to decide what your needs are.

Now let's look at it from the other side. How do you know you should climb to CFNetwork if you're thinking about using sockets instead? Well, use CFNetwork if you need integration with the run loop. You can do it yourself, but we've done it for you, so why not take advantage of the work we've done?

In particular, if you want to avoid managing the details of select, let us do that for you. Or if you want simple TLS or SSL integration, we can provide that for you. Or if you want some kind of a basic object abstraction, I've put object in quotes because core foundation is not a true OO language, but it does give you some object-orientedness. And in particular, it provides you with some reference counting.

So where can you find CFNetwork? CFNetwork actually appeared as a public API for the first time in 10.1, but we've added many new features starting with Jaguar. Here are the three big ones. We've added SOX support, that's SOX v4 and v5, CFNet services, of course, and we've moved from HTTP. They were 1.1 connections, but they were not persistent. Starting with Jaguar, we have full persistent pipelined HTTP 1.1 connections.

Okay, so with that I'm going to move on to start talking about some basic concepts. There are two basic concepts from Core Foundation that we leverage heavily, and it's going to be difficult for you to use CFNetwork without understanding them. Those are streams and run loops, so I'm going to breeze through the basics of those two types here. However, if you're not familiar with them, I highly recommend you come to session 808 tomorrow, Managing I.O. That session is all about using CFStream and CFRunLoop. We go into just tons more detail than I have time for today.

So streams. What is a stream? A stream is simply a one-way byte stream to some source or some destination. The APIs are very similar to simple POSIX file descriptors. You create the stream, you open it, you do a bunch of reads or writes. When you're done, you close it and dispose of it.

There are two true CF types to represent the streams, CF Read stream and CF Write stream, so that's one for each direction. However, as I go through this talk, I'll be talking about other types of streams, like a socket stream or an HTTP stream. Those are just special cases of read or write streams, and you should be aware that they are not true CF types in their own right.

Properties. So streams carry properties. That's the way you represent anything about the stream that is not actually part of the data transfer itself. properties describe how the stream is going to behave. They also represent any information discovered by the stream as it does its processing. So when you need to configure a stream, you're going to do that by setting properties on the stream. And when you need to get information from the stream, you may well do it by querying properties.

So how do you use properties? Properties come in name-value pairs. So you're going to access the property by name. The name is a CFString. And the value is going to be some kind of CFType. You should be aware that when you get and set properties, streams always have the right to refuse properties. Properties is a very generic mechanism. And if you ask a stream about a property, it doesn't recognize. It's simply going to say no or return null.

So a stream will refuse a property if it doesn't recognize it, if the value is invalid or somehow misformatted, or if the stream has already been opened. There are certain properties that you can set on the fly, but unless you see documentation that says otherwise, assume you must configure the stream, you must set the properties prior to opening the stream. So how do you know when a stream's refused a property? Well, copy property is going to return null. Set property will return false.

So here are some examples. In the first one, I'm asking an HTTP stream for the response headers it's read. Read stream copy property, pass the stream, and then the named property. KCF stream property, HTTP response headers. In the second one, I'm setting the security level on a socket to be negotiated SSL. This would be, for instance, the setting you'd want if you were trying to program HTTPS yourself. You'd set it to negotiate SSL with the server. Finally, I'm configuring an HTTP proxy on presumably an HTTP stream, and I'm passing in a proxy dictionary to describe that proxy.

Okay, so that's your brief 30-second introduction to streams. Now you're going to get a 30-second introduction to run loops. So run loops are also from Core Foundation, and they're represented by a CF type, CF run loop. And it's a basic abstraction for an event loop. The idea is that the run loop has several different sources, and in the course of its lifetime, it looks at those sources and waits for something interesting to happen. If something interesting happens to one of the sources, that triggers a callback. the callback handles the event.

So, for example, some typical sources you'd see on a run loop inside a running application would be sources to handle user events, mouse clicks, key presses, that kind of thing. Sources to field Mach messages coming in over IPC. Possibly timers, and then the one of greatest interest to us here, socket sources. Anytime we talk about a CF run loop, it's important to keep in mind there's exactly one CF run loop per thread. If you have multiple threads, you have multiple run loops. If you have a run loop, you're talking about a thread.

So here's the basic lifetime of a run loop. The run loop sits watching its sources. When something interesting happens, the run loop notices, triggers a callback. The callback will execute something, somehow process the event, and then return. And the run loop simply goes back to watching again, looking for something interesting to happen. The point behind all of this is it allows you to manage multiple inputs on a single thread. You have five sockets? Fine. Just install five socket sources on the run loop. The run loop is now watching all five sockets on a single thread for you.

Runloops and clients. So clients is a notion that CFNetwork and CFStream together use pretty heavily. The idea behind setting a client is you are setting your callback, your info pointer, and saying, hey, I'm interested in these kinds of events. Please inform me via the runloop. So to use a client, what you do is you first call setClient on the relevant object. CFReadStream setClient. You pass the stream, you pass your refcon, and then you pass a callback.

Then you take the CFNetwork object and you schedule it on a run loop. Remember, run loop is a thread, so essentially what you're saying is I want to handle events on this thread. CF read stream schedule with run loop, pass the stream, you pass the run loop of interest.

Typically you're going to be scheduling on the current run loop, meaning I want the callback on this thread that is executing now. CF run loop get current. And then finally the modes, which we'll get into more detail in session 808. I'm just going to use the common modes here.

So you can always schedule on multiple run loops if you want. Just call schedule with run loop multiple times. But remember, multiple run loops means multiple threads. The objects that we're talking about are not, in general, thread safe. You must make sure you are only accessing them from a single thread at a time. So if you've scheduled on multiple run loops, it is your responsibility to protect the objects.

So now what happens? I've taken this object, set my client on it, and scheduled it on a run loop. Well, as the run loop runs, the CFNetwork object is going to detect interesting events, bytes arriving on the socket, an error occurring, the connect occurring, those kinds of things.

As it detects those changes, it will trigger your callback from the thread. Now, the catch here is that if the run loop is not running, the CFNetwork object isn't looking for interesting events. So it is, again, your responsibility to make sure that the run loop is running if you've scheduled an object on it.

Now, the good news is that both the Cocoa and Carbon UI frameworks will run the run loop for the main thread for you. That's how it's watching for user events. However, if you spawn a secondary thread, or if you're not linked with one of these high-level frameworks, running the run loop is going to be your responsibility.

Okay, so for most CF objects, when you're done with them, you simply release them, and that's your signal to say, okay, I never need to hear from you again. Go away. That does not work with run-loop sources, because remember, they're holding a refcon back to you. And just because you have released your reference on them doesn't mean someone else isn't holding on to the CFNetwork object. And as long as that CFNetwork object is alive, it may well call back into you. So, how do you work with that? When you are done with the CFNetwork object, make sure to call setClientNull to tell the CFNetwork object, never trigger my callback again.

At the same time, you should also unschedule from any run loops to avoid causing unnecessary thrashing or running on the run loop. So it looks like this. CF read stream set client null. Unschedule from run loop, stream, the same run loop you scheduled on, the same modes you scheduled with, and then finally you're done. You can release the stream.

So that's it for the basics from Core Foundation. Now we're going to move into the meat of what the APIs inside CFNetwork provide. We're going to start out with socket streams. From core foundation itself, there are APIs that allow you to create a read stream and a write stream pair for a given socket.

You can specify the remote end of the socket stream these three ways, by host name, meaning domain name, and the port number. By the raw socket, maybe you already have a socket that you've configured from some other source. Or by the socket signature, in other words, by the sock adder. Basic support, like I said, is provided inside of core foundation, but CFNetwork adds a number of interesting configuration properties.

and, right. Okay, so you can find those properties inside cfsocketstream.h. One thing to keep in mind is with the socket, the read stream and the write stream are using that same underlying resource. They're both talking to the same socket. So if you configure the read stream, that's going to affect the write stream and vice versa.

And you should also keep in mind that you can set socket properties on any streams built on top of sockets, and we take care of passing it through. So for example, if you want to set the SOX proxy, which is a socket stream property, on an HTTP stream, just go ahead and do it, and it's the HTTP stream's responsibility to pass that property setting through. Now, this, I have to admit, is not quite working on the CDs you have. However, it will be working by Jaguar, really.

So what are the interesting socket properties? Here they are. The SOCKS proxy settings are one of the interesting properties. You can configure V4, V5. You can configure the remote address. If you want, you can construct the dictionary with these values yourself. If instead you want to just inherit whatever the setting is on the computer, on the current host, use system config to grab the proxy dict and just pass it blindly through. You don't have to look at it. We have made the formats match. The other interesting property that you'll find in CFNetwork is the TLS or SSL level. I'm going to move on now to talk about the HTTP engine inside of CFNetwork.

So, again, there are two basic CF types we provide for handling HTTP transactions. CF HTTP message is just a data object, represents an HTTP request or response. CFHTTP Stream, on the other hand, is the object that actually does the download, actually builds the connection to the server. So the normal way you're going to use the HTTP engine is you first create an HTTP message that represents the request. Then you create a stream from the HTTP message. Then you open from the stream and read the bytes off.

So, first CFHTTP message. Like I said, it represents the request or response. You can get and set the header fields, you can get and set the body, you can configure every aspect of the HTTP message. And when you use the HTTP message, there are two basic usage patterns. Either you're going to construct the message programmatically, that's the normal usage. You say, I want a request, it's going to be a get request, here's the URL, and here are the header fields.

Or you're trying to parse out a message from a series of bytes, so you know you have a byte stream that is coming from an HTTP server or that represents an HTTP message, and you just want us to go through the work of parsing out the HTTP structures.

So what does it look like? Here's how I construct a message. CFHTTP message create request. I pass the allocator. I pass the request string, so in this case I'm doing a post. I pass the URL that I'm interested in accessing. And then finally I pass the version string.

Now I've got my object, and I can go ahead and start setting header fields on it. So here I'm setting the user agent to be some string. You can set any number of them, obviously. And then at some point in the future, you may want to set a body on the request. CFHTTP message, set body, and here I'm just setting it to be some post data.

Here's the other usage. I'm going to breeze through this because this is the less common usage. But if you do have a series of bytes from an HTTP server and you're parsing it manually, here's how you do it. You'd create an empty message. You would go and get the bytes from wherever you're getting them from. And then as you get byte buffers, you would call HTTP message append bytes.

That function will return true, assuming the parse succeeded. If it returns false, it means the data you gave it was not valid. to validate HTTP data. Once you're done, you can now examine the message that you got, find out what the status code is, look at any of the header fields.

Okay, so much for HTTP message. Now let's move on to HTTP stream. You create an HTTP stream from an HTTP message, which represents a request. When you open and read from that stream, you're not going to get the header data. What you're getting is the pure body bytes. If you need to examine the HTTP header itself, you will do that by fetching a property like we saw earlier, CFStreamPropertyHttpResponseHeader.

CFHTTP Stream provides a number of properties that are interesting above and beyond the socket properties. You can find those HTTP-specific properties in CFHTTPStream.h. Here's a list of them. The ones that are settable are whether the stream should automatically redirect when it receives a 300 response. You can set the proxy settings.

That works exactly the same way as the SOX proxy settings. Just get the dictionary from system config and pass it blindly through if you simply want to use the proxy settings on the host itself. And you can set whether or not to use a persistent connection. The gettable properties are the response header and the final URL that was accessed. The final URL will only differ from the URL in the request if there was a redirection and automatic redirection was turned on.

So how do I use an HTTP stream? Well, create it and configure it, schedule it, Get the response in your callback, and then after that's all done, clean up. We're going to walk through each of those in detail. So here's the creation phase. Construct the request from somewhere, like we did before. Then create the stream. CFReadStream create for HTTP request. Pass the allocator and the CFHTTP message for the request.

Now do any configuration that's interesting. If you want to, for instance, if you want automatic redirection to go on, set it now. If you have a proxy dictionary you want to set, now would be the time to do that. When you're done, return the stream. So now we have a stream and we're ready to schedule.

As part of scheduling, you're going to need to set your client. The first part of setting the client is deciding what events in the stream lifetime you're interested in. So here I'm saying that the events I'm interested in are when the open is completed, when the stream is fully opened.

I want to know when the stream has bytes available that are ready to be read off. I want to know when the end of the stream has been reached. And I want to know if any error occurs along the way. Okay, now I'm ready to set the client. Set client, pass the stream, pass the events that you're interested in, then the callback, handle event, finally your context pointer.

Streams been configured. I've set my client. Now I'm ready to schedule on the run loop. CFReadStreams schedule with run loop. Here, again, I'm scheduling on the current run loop in the common modes. So I'm saying that as long as the toolboxes are just doing normal things, as long as the run loop has not been put into some kind of funky mode, I want my callback, and I want it on this thread.

Finally, I'm ready to open the stream. Now I just sit back and wait. The RunLoop and CFNetwork is doing your work for you, and all you have to do is wait for your callback to come in. So first I'm going to show you the signature for the callback. Callback takes three arguments. The first one is going to be the stream reporting the event. Second argument is what event has come in. And the last argument is your info pointer.

Most of the time, your callback is simply going to be a giant switch with a case statement for each of the events you registered for. I added the default line here with the just prints you should never get here. You will never receive an event you didn't register for. So now let's look at each of those cases in particular.

First one, CFStream event open completed. There's usually not a lot you want to do it when open completed. It's just sort of a nice milestone to know that the stream is progressing in its lifetime. So I'm just going to print that out. The open's completed. End Encountered. Now the stream has reached the end of its lifetime. I'm going to print out all done, and then go on to clean up the stream.

In the error case, I'm going to want to do some kind of error handling. I can get the error that's occurred back from the stream by calling CFReadStreamGetError, and then I'm going off into handle error, some error handling routine that I've written. Again, the stream is done at this point. Once a stream's errored out, you can't recover. So I'm going to clean up this stream.

And now on to the more interesting one, the has bytes available. This is where the most interesting work is going to be done in your callback. Before dealing with that middle code, I'm going to jump to the end. Every time I enter this callback, I want to get the bytes out of the stream that have been reported. I do that by calling CFReadStreamRead, pass in a buffer in the size of the buffer.

That causes the stream to copy in all of the bytes that it has into your buffer, and just like the POSIX read call, it returns to you the number of bytes that it's filled. And then I can handle those body bytes. Again, all of the HTTP headers, all of the HTTP control data has been stripped out. All you're receiving here are the raw data bytes carried by the HTTP response.

But often, you will have some special processing that you want to do when you first get the response header. So the way you do that is the first time you receive has bytes available, you know, you're receiving the first data byte, so the header itself must be complete. Go to the stream and ask for the response header. Copy property, KCF stream property, HTTP response header.

That's an HTTP message ref. You can examine it, look at what has happened, and here I'm calling process headers just to represent that work. Then when you're done, copy property has the word copy in it, so you received a reference. Make sure to release that reference by calling CF release.

That's it for handle event. Now the last stage is cleaning up the stream. I have gone into kind of painful detail here. Often in your own code, you will only need to do two or three of these steps, but I want to walk you through all of them and explain what each one is doing, and then depending on your situation, you will use some of these and maybe not others.

The first thing you may want to do is call CF Read Stream Close. That is your cue to the stream that it can release all of its system resources. You are done using the stream to transfer bytes. Now I want to guarantee that the stream never triggers my callback again. I want it to forget all knowledge it has about my info pointer and my callback function. Set client HTTP stream, no events, no callback, no info pointer.

Now I need to scrub the read stream off the run loops so that even though it's not calling me, it's not causing the run loops to spin at all. On schedule from run loop, I pass the same run loop and the same modes I used when I first scheduled. Current run loop, common modes. Finally, I release our reference to the stream.

So that's the most common usage of HTTP streams, but there is this other case that comes up from time to time. Sometimes you're going to want to send an HTTP request where the body is very large, larger than you want to hold inside of memory. Well, as you just saw, when we called CF Readstream create HTTP, we were creating from an HTTP request which had the body already attached. So in that case, the body was resident in memory. If you don't want to put the body in memory, use this function instead. Create for streamed HTTP request. And what you're going to do is provide a read stream that provides the body data.

When you open the stream, it will transmit the headers and then automatically open the stream you provided and start sending body bytes from it. There are a couple gotchas. If you provide a content length inside of the request headers, it had better be correct, basically. Otherwise, that's going to screw up the HTTP transmission.

Also, because we have no way of backing up your body stream, we cannot perform automatic redirection in this case. We couldn't somehow unroll the HTTP body content. And that's all I had about HTTP services. Now I'm going to invite Jeremy Wyld up onto the screen to talk about Rendezvous and Network Services.

So today I want to talk about network services, what they are, and how they pertain to CFNetwork. Think of network services as a method for advertising a service by name and type, much like MBP did. on a domain on the network, much like AppleTalk's zones. How do we do this?

This great new technology called rendezvous or zero configuration networking. This is brand new in Jaguar, and it provides a local networking protocol that's simple to use, like AppleTalk was, but it's also It's built upon industry standards. In order to get the nitty-gritty of this, I'd really invite you to go to session 811 on Thursday and really find out what we do there.

So what does Xero Configuration provide for you? It provides a standard for advertising services on the network. It's a name-based registration. You provide a name, a type, and it supports TCP and UDP. And then finally, you can register on any domain, so you're not stuck to a single registration domain. It also provides a standard for discovering services on the network, and on top of that, for discovering topology about the network, other domains that exist on the network, so you can find out about those other zones or domains that exist.

So what did we do for CFNetwork with this? Well, we created CFNet Services. We've abstracted two different types. We've created Net Services and Net Service Browsers. A net service represents the single service provider on the network. Some of the attributes for that service: your domain, your type of service that you're providing, your name of your service, its address or addresses, and then what port your service is listening on. The Network Service Browser then represents a search for either services or domains on the network, one or the other.

With these two types, they both follow the same CF type stream usage model. You're going to create them You're going to schedule them on a run loop. You're going to tell them to run. And then, finally, you just sit back and wait to handle the callbacks when these things occur. So, what do you do with these two objects?

Well, there's three main things that you're going to want to do. You're going to either want to advertise your service that you've created on the network, or you're going to want to advertise your service that you've created on the network. You're going to want to find services on the network.

Thirdly, you're going to want to resolve, connect to, use that service. What I'm going to do is walk through a simple application that's meant to be just an Echo server and then a client. I'm going to walk through this code, and we're going to do each one of these things. First off, let's start with advertising the service.

So, just like the usage pattern I stated earlier, we create the service. We create our object. In this case, creating it with the default allocator. We're going to, we're going to Create it on the default domain. This is indicated by the empty string. I'm registering a service of the echo type over TCP.

The name of my service. This may have either come from the system control panel, where the person set their name, or maybe I've prompted the user for it, or, however, you know, I just formulated this, pass it along. This is a CFString, can be formulated however you wish. Finally, what port I'm listening on. Now in this example, I'm listening on the well-known port for echo, which is seven.

Had I been using a real socket, what I could have done is queried the socket, asked it for its local port, and then used it instead. That's one of the beauties behind CFNetwork. The Rendezvous Protocol is that this port is no longer going to be important. What is important is the name and the service type that we're registering.

Next, in the usage model, we schedule it on the run loop. We've set the client, so I'm setting a callback, so my register callback, this context, which is going to hold my refcon and some functions, I schedule it on the run loop. Scheduling it on the current run loop in common modes, just like streams.

Finally, I tell it to run. In this case, we're telling it to register. This is going to go out, register the service on the network. We pass along the service that we want to register, we pass along a reference to an error struct. Should an error occur as a process of starting up the registration, we can find out about it this way. Now, registration and some of these other functions that I'm going to show are inherently asynchronous. In this case, we're registering. You can assume at this point you are registered on the network. You can assume so until you hear back on your callback.

And for registration, your callback will get called for one reason. There's been an error. There's really one important error situation that you really want to make sure you handle, and that is that there's been a name collision on the network. So at this point, what you want to do is formulate a new name, or maybe you just want to quit. I'd suggest formulating a new name.

So, you can formulate a name either by something as simple as adding a number to the end of your name that you used, you know, adding a two or a three, whatever, at the end. Or you can prompt the user again, ask them for a new name, choose a different name, that one's already existing on the network. And then we just go about trying to register the service again. Either way, we have to kill off our old service.

So we know it off just like we do streams. We make sure we pull it off the run loop. We go ahead and release the service. The service is gone at that point. You're no longer registered on the network.

[Transcript missing]

We have a service sitting out there, but people are going to want to be able to use your service. It doesn't do much good to create a service if you don't have people feeding on it. What we want to do is find services on the network. Once again, same usage model. We create our object. In this case, we're creating a browser instead of a service.

Create with the allocator. We're giving it a callback function and our context. The callback function is part of the creation because we have to do these callbacks. We have to be able to inform you that services exist, or domains exist, on the network. It's a part of the object's nature.

Finally, the context I'm passing in, you'll notice on my context that I'm using a mutable array. The reason being that as services come in, I'm going to add them to this cached list, or array of services. When I find services, I'm going to put them in this list. When they go away, I'll take them out of the list.

And because I'm using a standard CF type, I can use CF retain, CF release, and CF copy description for my context functions. So at this point, we go ahead, register it on the run loop, and then we're going to Registring on the current run loop in the common modes. Finally, we tell it to run.

So in this case, we're telling it to search for services. We're telling it to search for services, again, in a local domain, indicated by the empty string. We're searching for echo TCP types. And once again, pass along a reference to our error struct just in case an error occurs as a process of starting up the search. Had I wished to find out about network topology, I could have replaced this with a call for search for domains instead. And then I can search and find out other domains that exist on the network.

So we have this service registered on the network. We have someone else looking for it. Eventually, a user is going to find what they want. Oh, sorry. I better tell you about the callback. We find the services on the network. Like I said before, we're going to add and remove them from our cache list.

When your callback gets called, you're going to get called with the browser that you created. There's going to be a set of bit flags indicating many specific things. The type of thing that you found. In this case, we're searching for services, so we know we're going to be receiving services.

Had this been a search for domains, we'd be receiving domain strings instead. And finally, the error pointer, indicating an error if one occurs, or an info pointer, or your info pointer. In this case, we check to see whether we're removing an item or being told to add an item. This is indicated by one of those bit flags. Check to see whether we're removing it or adding it and do so.

And then, finally, we have this nice little flag down here called KCFNetService More Coming. This is used If you're on a large network, you're receiving hundreds of services from the network, or lots of domains. Instead of having one of those apps out there that flickers every time you receive one of these things, trying to do an update, what we'll do is we'll indicate with everyone that one's coming, unless we know that one is not.

If we get to the end of the list, what we can do is turn around and say, "Hey, I'm going Let's not pass along that flag. And now you can detect that, oh, this means that I can finally do the update. I can inform the user that it's okay, and they see their full list.

So we've got one application registering a service. We've got another one finding these services. Now we need to go out, connect to, and use that service. We do this very similar to the way we register our service. Once again, we create it. It looks very similar to the way we did the registration.

We're creating a service, local domain, Echo TCP, The name of the service that we found, but you'll notice this time I'm passing in a zero, because in this case the port is not important. We're going to ask the network for this information. We want to get the address and port of this machine that we're going to talk to.

We register and put it on the run loop, and then we tell it to resolve. Very similar to the registration, we're just going to ask the network now for that other machine's network address and port. At some point, hopefully it finds it, or it'll get a network error. But the thing to note is that this is an asynchronous call. It's not meant to necessarily complete.

I can say, find my printer that I've been using for months, and But because someone walked by it and decided to turn it off, I may not see it, and I'm going to tell it to go print. All of a sudden I realize it's not finding it. I can go down, flip on the printer, come back, it's found it. So at some point in the future, you're going to get your callback. Hopefully, you'll be told that there's been no error. It's returned an address.

So what do you do now? Well, you ask the NetService for its address. The one important thing to note here is that when you ask for the address, it's going to return a list of addresses. You've got to remember that these are multi-homing machines, right? So they can have multiple addresses.

In this thing, in this example, I'm doing one nasty thing in that I'm grabbing the first address, and I'm going to try connecting to it and it only. I would suggest you do something a little better. So in this case, I'm going to get the address that we want. I'm going to pass along to a routine to connect the socket and do whatever protocol work it needs to do.

Had there been an error, of course I want to go ahead and destroy the resolve. It didn't work. Remove ourselves as a client, remove it from the run loop, go ahead and release it, and it's gone. So everything that I've shown has been the asynchronous usage model, but sometimes that doesn't always work in your applications. Well, we support other threading models. So you can do a synchronous usage of CFNet services.

The difference being that don't schedule yourself on a run loop. Don't set the client. Don't set the run loop. You're now synchronous instead of asynchronous. Browser is still going to require a callback. It has to be able to inform you that these things are found on the network. It needs to know how to contact you to tell you that.

All the functions that run and perform will now block. So register, resolve, browser search for services, and browser search for domains are now blocking. And they will block indefinitely until, in the case of a register, an error occurs. In the case of a resolve, an error occurs or it finds an address. And then finally, search for domains and search for services will go indefinitely until they're canceled.

So, at this point, we'd like to show you some code and a demo of an application that was taken from the examples, developer examples app kit, and they're two things. They're called picture sharing and picture sharing browser. And, we're going to go ahead and What we've done is taken the code that they used, which uses the foundation form of CFNet services and SNet services, and a raw socket to transfer a picture from a sharing device to a browsing device. What we've done is gone and filled that all with CFNetwork instead. We're using CFStreams, CFHTTP messages, and CFNet services.

So the first thing that we're going to do is we're going to walk through the browser, the client application, and we're going to show CFNet services, Browsing for them, resolving them, HTTP to actually connect to the server and pull them down. So, first thing what we're going to do is create a new browser.

Like I said before, NetService Browser Create, default allocator, passing along a browser callback in our context, which happens to be ourself. This is a nice Cocoa app. If it successfully created it, get the run loop, we're going to schedule it, and we're going to start a search for services. Now, we're searching for HTTP services.

So we can have running on this machine services which handle HTTP, but they're not running as part of a browser. They're not Apache. They're some other application. And I don't have to tell my grandmother what port to go to. We're going to find it on the network. So we create our browser. We scheduled it. We're going to tell it to search for them, and at some point, we're going to get callbacks on our browser callback function.

So browser callback functions. Fires, we're going to be told, looks like very similar code to what I showed earlier. We're going to be told to remove items, or we're going to be told to add items. So in this case, I'm going to tell my object to either remove or add the services.

And those are just held in a list that I then later display in a table view. At some point, I'm gonna be told that there's no more coming, at which point I tell it to refresh, reload the data, ListView's gonna update and populate itself, and the user will be able to see everything that's in the known list of items at that time.

The user then sees that list. They're going to click on an item. They're going to click that service. So at this point, we've got a list of services. These are actual CFNet service types. I've been holding those in a list, so at this point what I can do is grab out the one that they clicked, right. I'm pulling it out by index.

I retain it just in case, while I'm doing this other lookup stuff, this service goes away. I'm still holding a reference to it. So I'm going to retain it. I get its name so that I can display a little status to the user, this is what we're looking for. We schedule it on the run loop. We tell it to go resolve.

At a point in the future, hopefully, we will be told that it resolved. At this point, what we're going to do is ask the service for its list of addresses. It's going to come back with a list. I'm going to pull out, once again, nasty code. I'm going to pull out the first one.

And what these things are inside the list of addresses are actual wrappers on SOC struct, they're struct SOC adders. So, I'm gonna pull that out. I'm going to take the IP address and more nasty code. I'm assuming AFInet opposed to AFInet or AFInet6 for IPv6, because it can be either one.

I'm going to create a string for the dotted IP, then what I'm going to do is create a URL based upon the IP. What we're going to do is do an HTTP to that URL, along with the port number that also came from the addressing. We're going to formulate that URL, and then we're going to create an HTTP message.

We're performing get request to the URL. We're doing one-oh HTTP. We're setting a header field to indicate who we are. We are the user agent picture sharing 1.0. We create our stream based upon that HTTP request that we just formulated. At this point, we schedule it on a run loop.

We opened the stream. That would be all the way down here, huh? As part of setting the client, we've set up our read events. In this case, what we care about are read events, so when it has bytes available, When an error occurs, and when it hits the end of the stream. So when there's no more bytes of data to be read. So at some point in the future, we're gonna get an event.

If it has bytes available, we're going to try reading off the bytes. The only case I care about right here is if it succeeds. If it doesn't, the event will still come through for whatever happened for the error or if it hit the end. We're going to append the bytes to a data buffer that we're building up.

At some point, we may get the end, or I assume we'll get the end, in which case that is the entire image data that we've now received from the browser, or the server. We build up an image from it, we're going to put it into our image view, and then display it.

Had an error occurred, we're going to stop the stream. In either case of reading in the stream or the error, we're going to close down our stream. So to close a stream, Becky showed it earlier, we remove ourselves as a client, we unschedule ourselves from the run loop, finally we close.

So at this point, let's show the application actually running. Here we have the browser. And in this case, we have a server that's sitting there. It's not even running. On another machine we've got three instances of the application sharing documents. You can see, we can click around, we can see what those are. Right? And it's downloading a very large, like, 4 meg TIFF or something across the wire, over to this other machine, just appears. OK?

So, You want to start yours up and see about dragging on something from the browser, maybe? Sure. Oh, we just appeared, you can see. So she started it, goes away, comes back. Click it. There it is. Just to prove that we are, in fact, doing HTTP, I'm going to copy the URL that appears down here and give it to Internet Explorer.

Ta-da! Internet Explorer can get that same image, but better. Now I'm going to grab an image here out of Google. I did the search a little earlier for pictures of the Golden Gate Bridge. I'm just going to grab one. drag it into my server. Now let's get IE out of the way. And I'm going to ask it to re-fetch this image for me.

There we go. All right. Now, I just have a few closing slides, and then we're going to move on to the Q&A. So to get more information about this, you should look at the sample code in Developer Examples Networking. There's a simple Echo server and client there that uses CFStream. There's also a little application URL load that shows how you can use CFNetwork, as well as NSURL or URL access to download URLs off the web.

I'd also like to point you at Developer Examples Cocoa, which includes the Cocoa versions of the picture sharer that we just showed. There's also a subscription mailing list hosted by DTS at Apple, macnetworkprog at lists.apple.com. If you subscribe to that, you're more than welcome to send any questions there. Both Jeremy and I monitor that list, and we'll happily respond to any questions.

And then I want to just highlight again the two sessions we talked about earlier. 808, which talks about CFRunLoop and CF Stream in much more detail than I was able to go into here. And then 811, for all of the deep, dark secrets of zero-conf and rendezvous. And with that, oh, right, also who to contact, Tom Weyer from Worldwide Developer Relations will be happy to take your emails. Again, the subscription list.