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: wwdc2009-107
$eventId
ID of event: wwdc2009
$eventContentId
ID of session without event part: 107
$eventShortId
Shortened ID of event: wwdc09
$year
Year of session: 2009
$extension
Extension of original filename: m4v
$filenameAlmostEvery
Filename from "(Almost) Every..." gist: [2009] [Session 107] iPhone OS N...

WWDC09 • Session 107

iPhone OS Networking

iPhone • 41:57

Efficient networking code is essential to an iPhone application's overall performance. Learn all about the networking infrastructure on iPhone OS, as well as the BSD and Core Foundation networking APIs available to you. Understand how to write solid networking code for low-power, high-performance iPhone applications.

Speaker: Josh Graessley

Unlisted on Apple Developer site

Downloads from Apple

SD Video (61.4 MB)

Transcript

This transcript has potential transcription errors. We are working on an improved version.

Good morning. My name is Joshua Graessley, I'm a Senior Software Engineer at Apple. I work in Core OS and I'm going to be talking about iPhone OS Networking. We'll go over a few things today. We'll start with the networking APIs that are available to you as a third party application on the phone; we'll cover some of the major differences from Mac OS X and we'll also go over some new features such as device to device communication; we'll cover some techniques for dealing with mobility and we'll finish up with some performance tips.

The iPhone OS has a full networking stack that comes straight from Mac OS X. There are a few changes mostly to address things like power, cover cases of mobility for transitioning from different networks all the time and to improve performance. And again the performance thing is really about improving power-- the fewer cycles we use, the less memory we use, the less power we end up using. The iPhone environment is a little bit different from the desktop environment. The desktop we can have multiple interfaces, additional interfaces that didn't come standard with the machine.

On the iPhone and the iPod Touch there's a standard set of interfaces. You have the WiFi interface; and in the case of the iPhone you have the cellular interface. There's also a Bluetooth PAN interfaces that will come and go now for the device to device communication; and we have VPN interfaces that can be set up. In iPhone OS 3.0 we've got VPN on demand is a new feature.

Finally, we only have iPv4; there is no iPv6, and that isn't because we don't like iPv6, we were just concerned about the resources that would take. In the future we will be adding iPv6 but we won't say when. We also have Bonjour in there; we've had Bonjour from the start. It's a fantastic technology that works really well over WiFi and we've done some cool stuff with it in iPhone OS 3.0. So this is just a diagram of a lot of the networking APIs that are available to you.

We have APIs at three different layers; there's the BSD Socket Layer down at Darwin, we've got Core Foundation and Cocoa. If you're really comfortable with Objective-C, the best place to start is the Cocoa Layer. If you're a little bit more comfortable with just regular C you can start with the Core Foundation Layer; and if one of these layers doesn't provide what you need you can always drop down to the BSD Layer.

So focus on sockets. We have Socket Layer APIs at the Darwin and Core Foundation Layers. Darwin is just straight BSD sockets. We have the standard name resolution APIs; getaddrinfo; getnameinfo, and then there's things like kqueue and select to watch for events on multiple file descriptors. At the Core Foundation Layer for dealing with sockets, most of what we have is all about integrating with the Run loop. CFSocket is a fantastic API that gives you a way to receive event notification, so when something occurs on a socket you'll get notified through a callback on your actual Run loop.

And CFHost is a good API for doing name resolution in an asynchronous manner so you don't have to block your Run loop. Moving up a little bit we have a Stream Abstraction Layer to stream APIs. These are not to be confused with the old Mentat streams. The Stream Abstraction Layer provides a way that you can interact with the PCP stream without actually touching the socket. The frameworks cover that.

It also adds support for SSL and TLS and it includes support for proxies to make that a little simpler. It won't automatically get the proxy settings out of the dynamic store or the system configuration framework. You'll be responsible for pushing proxy settings into these APIs and there's a CF proxy support API to get the proxy settings. At the Cocoa Layer there's an NSStream class and at the Core Foundation Layer the CFStream.

In addition to the stream abstraction we have support for a lot of the common protocols. At the Cocoa Layer there's the URL loading system, and at the Core Foundation Layer there's more CFNetwork. The Common Protocol support is FTP, HTTP, and HTTPS. At the Cocoa Layer we have NSURLConnection; NSURLResponse and NSURLRequest.

And at the Core Foundation layer we have CFStream and CFHTTP Message. These make it really simple to interact with these protocols and the documentation is really good. Actually I spent a lot of time using these APIs working on a new feature, and I found everything I needed in the documentation. Finally, we have Service Discovery.

Service Discovery is available at all three layers. At the BSD Layer there's the Bonjour API; and the Bonjour API actually has REM loop support built into it so you can use it directly from Core Foundation very easily. There's also a CFNetServices wrapper that simplifies the API a fair amount, but it also eliminates some of the options and flexibility of the lower level Bonjour API.

So if CFNetServices does what you like and you prefer that, it's fine to use that. If you need the flexibility that the lower level API gives you then that will work just as well. And finally at Cocoa, there's a NSNetService that's just like CFNetService. There's a few other miscellaneous APIs. We'll go over Game Kit right now and cover reachability in a little bit. Game Kit is a new feature of iPhone OS 3.0. It provides a few really neat things.

There's a user interface for setting up device to device communication; you can do voice chat as well as data sessions for games. The data sessions will only work over Bluetooth. Voice chat will work over Bluetooth and WiFi. One of the important things to remember is with the new device to device communication, Game Kit runs on top of Bonjour and the sockets.

If you want to use data sessions over Bluetooth and WiFi, you can just drop down to Bonjour and sockets and use that stuff directly and you'll get the Bluetooth and WiFi support. If you want to use the user interface, then you do have to use Game Kit but you will have this restriction that you won't be able to use game sessions over WiFi. It's Bluetooth only.

There are some things about the iPhone environment that are unique. When you're running as a third party application, you don't have any access; you don't get to run this root. So you can't do anything that you wouldn't be able to do running as root. For example, you can't open a routing socket and change the routing tables.

You get read only access to those routing sockets. There's also no access to setting up security policies or security associations through a PF_KEY socket; there's no access to BPF or Berkeley Packet Filter, so you can't write a network diagnostic tool that relies on that. There's also limited access to Raw IP; you can use an ICMP echo socket so you can do PNG but that's about it. And there's no access to low numbered ports; there was no access. In iPhone OS 3.0 we actually removed the restrictions so any application combined to a low numbered port.

There is a subset of system configuration framework available, the whole thing isn't there; primarily the reachability APIs, and the reachability APIs are really important. We'll cover that in a little bit. And there's a new CFProxySupport API. In the past you would use the system configuration framework to get the proxy settings to push into CFStream or NSStream; but now that you don't have access to system configuration, we provided a new API-- CFProxySupport.

It makes it a lot simpler. It provides notification when the proxies change. There are a number of new features in iPhone OS-- there's captive network support; scoped routing and Bluetooth device to device. Captive Network Support is an interesting new feature to handle the case where you're on one of these WiFi networks that requires some form of authentication.

When the iPhone gets onto one of these networks, performs a DHCP request and it gets back a DHCP response and it has an IP address, it has Gateway, it's got a DNS server-- it thinks, this is great, I've got Internet connectivity. But when it sends packets to the Gateway, the Gateway is going to drop those packets unless they're HTTP, in which case it's going to redirect them.

So if mail is running mail can't make any connections anymore. If something like the stocks App is running, stocks App can't get to any of its data because it doesn't know how to parse the response that it's getting back. This is really bad and we wanted to work really hard to try and avoid having a phone in this state any longer than absolutely necessary. So with Captive Network Support when the phone joins a WiFi network it's going to perform a probe.

And this probe is just a simple HTTP GET, and in the case of most captive networks that GET is going to be redirected and will detect if it was redirected. And we'll look at that redirect and we'll see whether there was a little bit of information in there that indicates that that hotspot supports WISPr-- Wireless ISP Roaming. It's a protocol that lets you authenticate automatically. In the event that the hotspot does support WISPr, we'll look in the key chain and see if we have credentials for this network. If we have credentials, we'll go ahead and try and authenticate without even getting the user involved.

It's really nice. It just works. In the event that the authentication fails or we have no credentials or the hotspot didn't support WISPr, then we'll fall back to something called the web sheet. And the web sheet is this little sheet, it's actually on the slide here, and it pops up over whatever application you've currently got; and it's got a web view in it, there's a cancel button, and you can go and do whatever you need to do to get online.

As soon as you manage to get online, the web sheet will detect that and it will slide away leaving you in the application you were in before. If you enter credentials like you use a name and a password, the web sheet will try and scrape those credentials and push them into the key chain so we can authenticate automatically the next time we associate with this hotspot.

In the event that we don't have a networking application in the foreground, then we don't want to show the web sheet because we don't want to bother the user. So we'll actually flip back to the cellular network and wait until a networking application runs; and then when that networking application comes up we'll show the web sheet. If the user cancels out of the web sheet or hits the home or lock buttons, the web sheet will go away and will disassociate from that WiFi network.

This does two things-- this gives WiFi another chance to associate with a network that will provide us Internet connectivity, and in the event there are no other WiFi networks that are known networks, we'll fall back to using the cellular network. So by the time the web sheet's gone the device is going to be back on the Internet.

Scoped routing is another new feature of iPhone OS 3.0. In the past the routing was fairly simplistic, it was based only on the destination address; and we've made a lot of changes to add intelligence to the routing algorithms. This will change the routing algorithm to look not just at the destination address but the source address as well; so we look at it and which interface did this come from, we'll send the packet out the same interface. This gives us a lot of flexibility now and there are a lot of services on the phone itself that take advantage of this.

Visual voicemail, for example, makes sure that traffic only goes out the cellular interface and it uses scoped routing to do this. With visual voicemail, the voicemail servers are only available on the cellular network in a lot of cases; since we can't get those over WiFi we need some way to be able to force the traffic over the cellular network even when WiFi is the primary interface.

With the push notifications, we use push notifications over cellular for power reasons; and because we never really know what kind of network we're on so we're never sure that we can get notifications back. With the cellular network we may behind in NAT but we know a lot about the NAT so we can make some intelligent decisions about how often we have to send keep-alives and whatnot.

Exchange support does the same thing for push notifications. They force these things over the cellular network; and Internet tethering will also use scoped routing to cause the traffic to go over the cellular Internet, so if your phone is on WiFi that you've got your Mac tethered, the tethering will actually go through the cell phone network while your iPhone is using the WiFi network. And finally, Game Kit takes advantage of this.

With Game Kit and device to device, the device to device interface will come up and Game Kit will bind the socket to the right interface to force the traffic out that Bluetooth Pen interface. This is the feature that was developed for the phone but is actually new in SnowLeopard as well, and it has a number of benefits on the desktop OS. For servers you can do load balancing across multiple interfaces a lot better because the traffic will go out the right interface.

So this is a little diagram to kind of help explain how this stuff works. If you've got an application running on the iPhone and were connected to the cellular network, when the application goes to perform a connect, the networking stack will give it a socket that's bound to the IP address from the cellular network-- in this case we've got 10.1.2.3.

Traffic's working fine, everything's great. When the WiFi interface comes up, the default route is going to switch to WiFi and that's going to be a problem. When the networking stack looks at the destination, it will see, oh, the server's on the Internet. My primary route to the Internet is over the WiFi interface, so it will send the packet over the WiFi interface. The problem is the packet's going to have the source address from the cellular network.

And when that packet goes out on the WiFi network, the WiFi network is going to drop it. The application won't get notified and the networking stack doesn't get notified either. At some point in time your connection's going to time out, but your user is going to be sitting around waiting, going, "What the heck is going on?" With iPhone OS 3.0 we've made the routing a lot more intelligent. New connections will go over the WiFi interface and follow the default route, but existing connections, when they send a packet, the networking stack will look at the source address and go, oh, that's a source on the cellular network.

I need to make sure that packet goes out the cellular interface and the connection will remain over the cellular interface. You won't gain any advantages of the fact that there's a WiFi interface available, and you can actually run into some problems here. So your connection is going to be slower because cellular is usually slower; in some cases it might be faster but it's safe to assume that if WiFi is up you should try and use that. With the WiFi interface up and the cellular interface up at the same time you're draining power faster than if you were just making use of the WiFi interface.

So if you can that's really good to try and switch over to WiFi. Coping with routing is fairly hard. It's a challenge to get right. Reachability API will save you. It has the information that you need to write your application properly. It's not simple to use, but if you get this right it really shows. Reachability will let you know when there's a change.

When you see one of these changes from reachability you need to look at the networking environment and decide if it makes more sense to reconnect or stay with the current connection that you have. Scoped routing will actually cover up some of the mistakes that you might have caught in the past. For example, when WiFi comes up the network connection in the past would have just timed out after a while; and in testing you probably would have seen that and caught that.

Now it will just keep working but it will work slower and will be draining power faster because we're using both interfaces. So how do we fix this? With reachability you'll get a lot of information about when there are changes to the networking environment. Networking is really easy to write code that kind of works when you're sitting in your office and you have a great Internet connection.

DNS is always responding; the server is always responding; everything is great. When you go out into the real world and you're switching back and forth between WiFi and cellular or you're going through a tunnel on the train and you lose all of your connectivity, it's a lot harder to cope with these changes. Reachability gives you information about when these events occur.

It's not something you should use as a pre-flight check; this is a common mistake. What you want to do is you want to go ahead and perform your connection first, and at the same time start up reachability to watch the host that you're interested in connecting to. Your connection will succeed or fail. If it fails you'll get a reachability notification when it might be reachable so you can try and connect again at that time.

If it succeeds, you'll get a reachability notification when there might be a different way that you can connect to it. So having that reachability there is really useful, but it's also important to remember to connect first. If you do your reachability first and then connect later, you're just adding time before your connection's going to complete. The other really important thing to note is that reachability lets you avoid polling.

If you didn't have a reachability API available to you and your connect failed the first time, you'd have to pick some interval that you would retry after, so you could go with 30 seconds. That's going to take a fair amount of power, you might back off to a minute, but then if there's a change, it's going to take a whole minute for you to notice. If you use reachability, you'll get those notifications immediately and you can respond to them.

The reachability API is fairly straightforward. There's a NetworkReachabilityRef that you'll need to make use of the API. You can create one of these ReachabilityRefs using two different functions. There's a SCNetworkReachabilityCreateWithName and SCNetworkReachabilityCreateWithAddressPair. The CreateWithName version is really good for watching the remote host that you're interested in. Once you have established a connection, you can get the remote address and the local address and use ReachabilityCreateWithAddressPair.

And that will let you know when the connection you currently have is no longer viable. So for example, if we changed from WiFi back to cellular, the connection that you had over WiFi is no longer viable. Your SCNetworkReachabilityCreateWithAddress Pair would indicate that the reachability is gone; but your SCNetworkReachabilityCreateWithName would indicate that the reachability is still available. It would indicate to you that you need to tear down your current connection and connect again in order to have connectivity. Once you have one of these ReachabilityRefs it's really important to use the asynchronous API.

The first step is to set a callback function; and this callback function will get called anytime there's a change of environment or reachability. The flags may not always change but there may have been an underlying change. For example, if the reachability is still there, but we're over a different interface like the VPN interface came up, you would get a callback but the flags won't be any different. Once you've set your callback you'll need to schedule the ReachabilityRef with a RunLoop so you can specify which run loop the callbacks will occur on. And once you've done that you can call ReachabilityGetFlags and that will tell you what the initial state is.

A lot of times if you're using this with SCNetworkReachabilityCreateWith Name, the Flags will come back 0 because we haven't finished doing the host name lookup so we don't really know what the reachability state is yet and that's perfectly fine. Just wait for the callback to let you know when that changes. When you do receive one of these notifications it's really important to evaluate what's going on and decide whether you need to reconnect or stick with your current connection.

This is a little cheat sheet of the most interesting flags that you'll get with reachability. When reachability notifies you of a change, it will set a bunch of different flags to let you know a little bit about the current state. The kSCNetworkReachabilityFlagsReachable indicates whether or not the working stack thinks that we might be able to connect to this remote host.

It's really important to remember this doesn't mean that you're absolutely going to be able to connect. This doesn't take into account anything about firewalls or Internet problems between your device and the actual host. It's just the local network. This indicates we have a route to get to the Internet; we think we can get to it, we don't know for sure.

The only way to know for sure is to actually connect. kSCNetworkReachabilityFlagsConnectionRequired indicates that the cellular interface may not be up right now or the VPN on demand interface is not up right now, so we're going to have to do some sort of connection before we can get to the host. But you should go ahead and try and connect using one of the CoreFoundation or Cocoa APIs to trigger that connection to come up.

In the case of CoreFoundation, you can use the CFSteam and the higher level protocol APIs. In the case of Cocoa you should be able to use all of the APIs. The CFSocket APIs will not trigger cellular to dial or VPN on demand so you need to find some other API. If you really want to use the CFSocket API, you can do that but you're going to have to open some other connection first using the CFStream API.

And finally, there's the kSCNetworkReachabilityFlagsIsWWAN and this will indicate whether or not your traffic is going to go over the cellular network. Even if we have a VPN connection, we'll still indicate this Flag if that VPN connection is over the cellular network. There's a UIRequiresPersistentWiFi-- it's a key that you can set in the Info.plist for your application. This has cause a bit of confusion among developers.

This really means, "I'm a network application." And by setting this in your Info.plist it's going to tell the phone that when your application is running and in the foreground, it's OK to do whatever we can to get you onto the network. If you have an application that doesn't really have anything to do with networking, we don't want to be popping up dialogues or bothering the user. So setting this will let us do a lot more to try and get you online. This will disable the WiFi disassociation timer.

Normally WiFi will disassociate after I believe about 30 minutes. This will also allow us to show various WiFi UI so we can show the password dialog in the event that we associate with a WiFi network where we don't have the password; the ask to join dialog in the event that the user hasn't turned that off yet and they happen to be in range of the WiFi network; and also the new captive web sheets. Bluetooth Device to Device is a really cool new feature of iPhone OS 3.0. It's pretty amazing.

It will actually work with existing Bonjour applications on the phone; and it's all done using Bonjour over Bluetooth device discovery so the Bluetooth packets that are used to discover other Bluetooth devices out there, we've kind of put a little bit of extra information in them to indicate that we support this Bonjour discovery.

And it allows Bluetooth devices to talk to each other and actually ask the other Bluetooth device, "What advertised Bonjour services do you have available?" At the point that we're advertising these services, we don't even have an interface up, there's no IP address or anything. We don't actually bring up an interface until you go to resolve the service; and that will bring up a Bluetooth PAN connection and we'll get an IP address and the process takes a lot of time. So resolving a service is a very expensive operation.

Even doing browses for services is fairly expensive because we have to generate this Bluetooth traffic. So all of this is implemented using Bluetooth device discovery and then Bluetooth PAN for the actual communication. There are some limitations on these Bluetooth PAN connections. We can only have a few connections. There's a maximum of one outgoing PAN connection, and a maximum of three incoming PAN connections. And in order to get rid of these connections so we can start new connections, we have an idle disconnect set up.

The idea behind this was that we were trying to target somebody that might be playing a game, and they're either hosting a game or joining a game. And if they're hosting a game they can host and allow up to three incoming connections. And if they're joining a game they really only need a single outgoing PAN connection. These connections are bidirectional; it's just a question of how you actually initiate the connection.

There are issues with Bluetooth and WiFi coexistence. Both Bluetooth and WiFi run at 2.4 gigahertz; there's a single antenna in that phone, there's not a lot of space so when Bluetooth is using the antenna, WiFi can't. This means that when you're doing your browsing or resolving of a service over Bluetooth, there's going to be a performance hit to WiFi. And the other thing, Bluetooth Device to Device isn't supported on some of the older devices.

There's a session tomorrow that we'll go over some of that and the Game Kit device to device. Let's switch gears and cover some performance things. We'll go over some of the issues with the Simulator; we'll go over latency; we'll cover some non-blocking IO and some issues with multiple threads and finally CFSocket.

The Simulator is a great place to do rapid application development. It lets you compile some code, run it, see how it behaves; stop it in the debugger; make some changes; rerun it, very easy. You don't have to actually have an iPhone to do any development. But this does nothing to simulate the network environment you're going to run on the phone.

You really need to be testing your applications on the phone itself. On the iPhone you have a WiFi connection but it may not be as fast as your desktop's WiFi. You've got the cellular connection, 3G and EDGE, which is definitely not as fast as your WiFi; and you've got VPN connections that can come and go with VPN on demand.

If you can get VPN on demand set up, I highly encourage that because it may-- it's really good to see if your application works well in that environment. And check to see what happens when you're roaming between WiFi and cellular. You can catch a lot of interesting bugs there. And your customers will really appreciate that you put the polish in to handle that case well. The other big differences you run into are the CPU.

CPU on the desktop of course is a lot faster. There's a lot more memory which means we set a lot more aside for network buffers; so you can run into some differences there. And finally, the latency and the bandwidth of the networking on the iPhone is very different from the desktop.

So latency is something that will kill your performance if you're not paying attention and you don't work with it. The cellular round trip times are very long. It's a little bit better than 1/3 of a second on EDGE, and a little bit better than 1/6 of a second on 3G. Latency really will kill your performance.

If you want to go and open a TCP connection and send a request to get some sort of data back, first you have to do your DNS lookup-- that's 1/3 of a second gone right there. You've got you TCP three-way handshake that's going to be another 1/3 of a second that's just gone; and finally you send your request which is going to take another 1/3 of a second before the response can come back.

That's a whole second that you've just blown on a simple connect. Latency will kill you. HTTP is latency bound. Safari over 3G was originally only about 2 times faster than EDGE. They actually spent a lot of time optimizing this and they did a great job of improving it.

We'll go over some of the things that they managed to do. This is a simple diagram showing how latency can affect your connection; and we have an example HTTP connection on the left hand side without pipelining; and on the right side we have HTTP with pipelining. So without pipelining we do our DNS request, that's 1/3 of a second, and we've got our TCP connect with another 1/3 of a second.

We do an HTTP GET for the route document; that's another 1/3 of a second; we price that document and we see what resources we need to do, and then we do a fetch for another document for another resource, we do a GET for x, and when that comes back we do a GET for y. We've blown quite a bit of time here and these gaps between the GETs are wasted bandwidth. We could have been using the pipe but we left it empty. It's kind of like bubbles in the processor; you're looking at getting the best performance out of a processor.

On the right hand side we have HTTP with pipelining and from the beginning it's pretty much the same. We still have to pay the cost of doing the DNS; we still have to pay the cost of doing a TCP connect and we still have to pay the cost of the HTTP GET for the route document, but now when we do a GET we can specify more than one resource; so in this case we say GET x and y. And as you can see there's no gap between x and y coming back so we've got a performance win there.

For 2, this isn't really a huge win but when you're talking 10 or 20 resources it really adds up. And to demonstrate that we have a kind of gross chart but if you focus on the timeline at the bottom, this is an HTTP GET with the NY Times. It took about 20 seconds to pe rform this GET.

This is using Safari. If we do the same GET using pipelining it drops down to a little over 6 seconds; and another interesting thing about this is that we don't actually get the send back for the first 2 seconds. And the cellular interface was actually asleep when I ran this to capture the packets. So it takes the cellular interface sometimes up to 2 seconds to come out of the low power mode.

So pipelining is a huge win here. We went from 20 seconds to less than 5 seconds. This is looking at the TCP connection. Each of these little blips is a packet as it comes in. This is the HTTP Receive without pipelining. You can see in the first 5 seconds we managed to send about 40 kilobytes. And if we zoom in we really don't see all that much out of this connection. It's pretty slow.

When we use pipelining, we have a significantly faster connection. In the 5 to 6 seconds that we had initially, we were able to get all 128 kilobits, so pipelining is huge. If you can take advantage of it you really should. If you're designing a new protocol you need to take into account the affects that latency is going to have. If you're designing a protocol, allow multiple requests to be outstanding.

It's really bad to have request A then wait until you get A back, then request B and then wait until it comes back. It's much better to have a protocol where you can request A, then request B, then request C and request D because the server can start sending all that data back to back. HTTP pipelining unfortunately is a little bit busted.

Safari doesn't actually take advantage of it because there are proxies out there that will break it, and there are some Apache plug-ins that can also cause problems with this. If you're running your own server, you can make sure that you don't have the plug-ins that are going to cause the problems, and you can be aware of when there are proxies being used.

Although there are some proxies you can't detect but for the most part you should be able to detect them. If you can take advantage of HTTP pipelining you're going to get a huge win. Sometimes you work around latency problems by opening multiple connections and that's actually the approach that Safari takes. Multiple connections aren't ideal because you have this problem with TCP.

TCP really works well when you can cram as much data as possible into it. It lets the congestion window open up quickly and it keeps data streaming at the maximum speed it possible can. If you have a bunch of little connections and on each connection you get a little bit of data every here and there, the congestion window never really opens up, TCP never really performs as well as it can so you end up losing bandwidth. The other thing is with multiple connections it's really hard to tune. You have to figure out exactly how many connections you want to make and then figure out where the ideal place is.

It's not good. The other thing to note is investigate why you are getting the performance that you are. In the case of Safari with the original iPhone, it was good. When iPhone 3G came out we saw that it was only 2 times faster and we're like, "Well, 3G should be significantly more than 2 times faster than EDGE." And we spent some time investigating and realized that we were running into this latency problem; and the Safari guys did a lot of great work and managed to improve that performance quite a bit; although they weren't able to get the pipelining in because of the concerns for compatibility.

Another thing to keep in mind, don't ever use blocking IO, especially on your main thread. If you block the main thread doing a DNS request for example, that can take up to 30 seconds to time out, and there's a watchdog timer to make sure your application is responding, and that will wait for about 20 seconds.

So if you do the math you're not going to make it. The socket read/write and connect calls that block can also take a very long period of time; so don't do any blocking IO on your main thread. If you need to do blocking IO, spin it off to another thread, or even better, just use non-blocking IO. The other thing to keep in mind is it's really good to avoid these modal connecting dialogs.

If somebody's network connection is not working, they're going to be a little bit frustrated; and if your application has this dialog up that says oh I'm connecting, well yes, the network's down, I'm really frustrated, I can't interact with your application. Don't put up a modal connecting dialog; let people interact with your application but do put some indication that it's trying to perform a connection. Safari is actually a great example of this.

When you go into Safari and you put something in the URL bar and start loading, Safari still remains responsive. You can go into the bookmarks, you can bring up one of the other tabs or windows, you can go into the Google bar and put something in to change it; you can cancel; it remains very responsive no matter what the network is doing. If you can model your application after that, people will appreciate it. And this can be a little more contentious subject but when you're working with threads, threads can make it a little bit easier to work around synchronous APIs. Threads can be very resource intensive though.

When you create a thread, it's creating some place to store a bunch of state and it does this by allocating a stack; and the stack is going to be pretty big because the OS doesn't know how much state you're going to be using in that thread. So it conservatively sets aside a lot of memory.

There's also overhead when you're trying to synchronize between different threads; and while this can be a little bit easier to write, the threaded model is really better for CPU bound workloads, so if you're stuck cramming numbers on the CPU, then threads are fantastic because it lets the operating system offload that work on the multiple cores at the same time.

When it comes to stuff that's I/O bound it's a lot easier or a lot more efficient to use something like an asynchronous state machine. You can allocate the minimal amount of resources possible because you already know how much state you're going to need. This will give you better scalability.

If you wanted to write an asynchronous DNS server that was going to handle-- or a DNS resolver that was going to handle, say, 1,000 simultaneous requests, you can't just go and spin off 1,000 threads. That would not work. But if you're allocating just a tiny amount of space, a little bit of state for each request that you're doing, you can easily allocate 1,000 of those and you can have all of these operations going on at the same time.

It is a little bit more complicated to write because you have to keep track of the state yourself, but it does scale better. One other thing to keep in mind the iPhone and the iPod Touch only have a single core; so putting something on multiple threads doesn't really mean that you're going to get a big performance win.

There's not another core to go offload this work onto. You're just going to give the scheduler a lot more to do. CFSocket is a wonderful API. It does this very complicated thing of letting you work with sockets on a Run loop. Run loops are all based on mach ports and sockets of course are file descriptor based.

And there's no primitive on the operating system that lets you wait for an event on both a mach port and a file descriptor. So CFSocket goes and spins up another thread for you and in that thread it will wait for all the file descriptors that you're interested in.

And when it gets notification that an event has occurred on one of those sockets, it will actually use a mach port to signal the run loop thread to let the run loop thread know hey there's data available on this socket. It's really solid, it's been hammered on for years, it's a great API to use. There is a catch though.

When you're using this API you need to keep in mind that there is some overhead for that back and forth; so when you get one of these callbacks that lets you know hey there's some data available, make sure that you use a non-blocking socket and make sure that you read everything you can out of the socket.

If you read 10 bytes and there's 1,000 bytes on a socket, then as soon as you return from your callback, the callback's going to signal the CFSocket thread and say hey you can go ahead and put this back on the file descriptor list; I'm interested in knowing when there's an event ready.

The file descriptor thread will signal immediately because there's already data waiting on that socket, and then it's going to send a mach port message back and it's going to call your callback. So you're right back where you were but you've just blown a whole bunch of cycles and a bunch of power. So try and read everything you can until you get the EWOULDBLOCK error. This isn't applicable to CFStream.

CFStream does some things under the covers to be a little bit more intelligent. On SnowLeopard with lid dispatch and Grand Central dispatch, GCD, this is actually a lot better for CFSocket. CFSocket doesn't use this extra thread anymore. It's all implemented using lid dispatch. Some day in the future the iPhone OS is going to make that migration and CFSocket will get that same win. So if you write your code for CFSocket today, you're going to get that performance win automatically in the future.

So I'd really encourage you to use CFSocket. Finally we have some miscellaneous tips. Don't assume a performance advantage. WiFi is not always faster than 3G and 3G is not always faster than EDGE. You may have noticed in some of the conferences that 3G can sometimes perform slower than EDGE has when you don't have thousands of developers with the same phone.

And then WiFi networks of course you never know what's on the other side of the WiFi. You've got Apple shuttles that have WiFi on them which are fantastic except there are 30 or 40 people that are using the same WiFi connection, and on the other side of that WiFi connection is a cellular connection. So sometimes it's faster to just pull out your phone and get onto the cellular network yourself. Please remember that WiFi will power off unless you set that UIRequiresPersistentWiFi, the 30 minute timer.

Will also power off WiFi as soon as you lock the screen. Remember that in order to trigger cellular to Dial or the VPN on demand, you need to using one of the Core Foundation APIs; not CFSocket, but CFStream or a higher level API; or one of the Cocoa APIs.

And one other thing, if we could get you to connect your UDP sockets we'd really appreciate it. With a UDP socket you can open it up and you can use send to and specify the destination address. If you, on the other hand, connect the socket, then we know what the destination address is. And when we're dealing with these Bluetooth PAN connections, we can know that that socket wants to use that Bluetooth PAN interface.

And in the future when we have all these connected sockets, when you go and remove your socket, we can keep sort of a reference count on how many sockets you use in that interface. And when it reaches 0 we can disconnect the PAN Bluetooth device to device connection really fast. We won't have to wait on some idle time out.

So we'll get better performance and we'll save power. So in summary, please use reachability. Reachability is really important. Your users will really appreciate it if you get it right. It is really hard to get it right but it's worth it. Always use non-blocking IO. You don't want to block your main thread and get killed by the watchdog; it's kind of embarrassing. Be aware of latency. Latency has a huge performance impact; probably the biggest performance impact you'll run into on the cellular network. Don't expect it to be slow. Cellular networks are not slow.

They have high latency but they're actually pretty fast. Please test on the iPhone itself. The iPhone is an environment unlike the desktop. The simulator is great for simulating what the UI is going to be like. It's not great for simulating what the network is going to be like; and see what happens when you unplug your WiFi router and the phone falls back to cellular. It is your application, handle it properly. See what happens when you've got VPN on demand.

Assume that your network environment's going to be dynamic; even if you don't get a reachability change notification, your bandwidth can change as the congestion changes. If you're sitting in here, you're the first one in here, nobody else has been let in yet, you've probably got fairly good WiFi or cellular or whatever, and as everybody comes in there's going to be more congestion and your connection is going to slow down as a result.

That's just inevitable. Your application needs to be able to handle this and deal with the fact that your connection is slowing down. Please connect those UDP sockets so we can do something smarter in the future with those Bluetooth PAN connections, and be sure and read all of your data out of CFSockets when you get your callback so we can avoid spinning.