"Silence is the virtue of fools." [Why the Windows Phone 7 Emulator can sometimes take forever to access the network - and how to fix it!]
If you've used the Windows Phone 7 emulator to access the network, you might have noticed that in some environments it takes an absurdly long time for network activity to complete. For example, a single HTTP request with Internet Explorer or an HttpWebRequest from a custom Silverlight application can take upwards of 20-30 seconds to complete - even when the remote server responds immediately.
Now, a lot of you probably haven't experienced this problem and have no idea what I'm talking about - but I bet those of you who have are eager for a solution! I had to deal with this myself, and I know it makes developing a network-oriented application exceedingly frustrating...
Fortunately, there's a fix! :)
The Cause
I got so tired of the delays one evening, I decided to install Microsoft's Network Monitor (AKA NetMon) so I could look at what was happening at the network level. (Aside: Network Monitor is a fantastic tool for sniffing packets. Another great program (which works cross-platform) is Wireshark.) So I ran a new instance of the emulator, started the trace, and opened IE (which navigated to its default page). The problem was immediately evident from the timestamps (the left-most column below) and the nature of the traffic:
15.2837914 DNS:QueryId = 0xE7D1, QUERY (Standard query), Query for go.microsoft.com of type Host Addr on class Internet 15.2838039 DNS:QueryId = 0xD4AE, QUERY (Standard query), Query for go.microsoft.com of type AAAA on class Internet 15.3120594 DNS:QueryId = 0xE7D1, QUERY (Standard query), Response - Success, 64.4.11.160, 96.6.112.198 16.2787382 DNS:QueryId = 0xD4AE, QUERY (Standard query), Query for go.microsoft.com of type AAAA on class Internet 17.2787318 DNS:QueryId = 0xD4AE, QUERY (Standard query), Query for go.microsoft.com of type AAAA on class Internet 19.2787138 DNS:QueryId = 0xD4AE, QUERY (Standard query), Query for go.microsoft.com of type AAAA on class Internet 23.2788677 DNS:QueryId = 0xD4AE, QUERY (Standard query), Query for go.microsoft.com of type AAAA on class Internet 27.2923071 DNS:QueryId = 0x91B, QUERY (Standard query), Query for go.microsoft.com of type AAAA on class Internet 28.2888689 DNS:QueryId = 0x91B, QUERY (Standard query), Query for go.microsoft.com of type AAAA on class Internet 29.2888390 DNS:QueryId = 0x91B, QUERY (Standard query), Query for go.microsoft.com of type AAAA on class Internet 31.2889078 DNS:QueryId = 0x91B, QUERY (Standard query), Query for go.microsoft.com of type AAAA on class Internet 35.2889120 DNS:QueryId = 0x91B, QUERY (Standard query), Query for go.microsoft.com of type AAAA on class Internet 39.3462453 HTTP:Request, GET /fwlink/, Query:LinkID=192163
The trace above is just a snippet; the total time taken from when IE began its navigation until the (very basic) page had completely loaded was 182 seconds, or just over three minutes!
Looking a little closer, we see that Windows Phone 7 (and/or the emulator) sends out two DNS queries for the IP address of the go.microsoft.com
server corresponding to the IE home page URL. The IPv4 query returns immediately, but there's no response to the IPv6 query. At this point, two things are going wrong:
-
Windows Phone 7 has received a valid IPv4 response to its DNS query and could proceed with the HTTP request. But it doesn't... Instead, it delays everything waiting for an IPv6 reply to its query - and eventually times out (presumably) after about 25 seconds. While I'm not aware of a specification that covers this scenario, it just plain seems wrong. If the network stack prefers to have an IPv6 address "on file", that's fine - but there seems to be no need to delay everything waiting for it. A better approach might be to proceed with the current transaction using the valid IPv4 address that's already been returned - then switch to the IPv6 address later on if/when it becomes available.
-
The DNS server should reply to the IPv6 query with either a valid response or an error code - and not ignore it as though nothing happened. In this case, the DNS server happens to be my DSL modem, which I suspect knows nothing about IPv6 and is therefore unable to return a valid IPv6 address. However, that's no excuse for ignoring the IPv6 queries - according to my reading of RFC1035, it should respond to these queries with a "Format error" response code (or similar). If it did, Windows Phone 7 would presumably process the failure and move on with its life instead of hanging around waiting for a reply that never shows up...
This trace also explains why some people experience the slowness and others don't - if the IPv6 query gets a reply (instead of timing out), the network transaction proceeds immediately. So if you're fortunate enough to have a well-mannered DNS server (as I am at work), you'll be fine. But if you're unfortunate enough to have a poorly-mannered DNS server (as I am at home), you'll be spending a lot of time twiddling your thumbs waiting for the network...
The Fix
Fortunately, there's an easy fix: get a better DNS server! :) Either reconfigure your (or your company's) DNS server, upgrade its firmware, or flat-out replace the darn thing and you'll be set. Unfortunately, most people don't have a lot of control over the networking hardware/configuration they're using - or maybe they're just not eager to mess with this stuff. No problem - there's another fix: swap out the DNS server your computer is using for one that works better!
But where can you find a better DNS server? As it happens, there are a number of free DNS options already out there, just waiting to be utilized! The most well-known might be OpenDNS, but there are others like Google Public DNS, etc.. In my experience, OpenDNS and Google Public DNS both handle the IPV6 DNS request immediately - just like we want. So all you need to do is point your computer to one of these DNS servers, and things should work better!
The Details
By default, most home computers get their DNS server address via DHCP from their router or modem. So you could change the DNS server address in the router/modem configuration and that ought to work fine. However, you might not have access/permission for that - and besides it would affect everything else on the network. Unless you're a network administrator, this path may be more trouble than it's worth...
The good news is there's an easy way to change the DNS settings for a computer's network connection within Windows - and it's even easier to automate! To make this change in the UI, open the Network and Sharing Center (either via the Start Menu or the Control Panel or right-clicking the network icon in the system tray), click on the active network adapter (typically "Local Area Connection" or "Wireless Network Connection", click the Properties button in the lower left of the dialog, click the "Internet Protocol Version 4 (TCP/IPv4)" item, click the Properties button below it, select the "Use the following DNS server addresses", type the IP addresses of the "Preferred DNS server" and "Alternate DNS server", and hit OK/Close a bunch of times. To undo the change, repeat those steps, but choose "Obtain DNS server automatically" instead at the last step.
Okay, the UI approach may be pretty, but it's hardly efficient. :) If you're going to do this more than a couple of times, you may want use (or customize) one of one of the following scripts I've written based on the Windows netsh utility and the IP addresses from the corresponding Wikipedia entries (please don't forget to change the adapter name if you're using a wireless connection):
D:\T>type DNS-OpenDNS.cmd @echo off netsh interface ipv4 add dnsservers "Local Area Connection" 208.67.222.222 index=1 netsh interface ipv4 add dnsservers "Local Area Connection" 208.67.220.220 index=2 netsh interface ipv4 show dnsservers D:\T>type DNS-GooglePublicDNS.cmd @echo off netsh interface ipv4 add dnsservers "Local Area Connection" 8.8.8.8 index=1 netsh interface ipv4 add dnsservers "Local Area Connection" 8.8.4.4 index=2 netsh interface ipv4 show dnsservers D:\T>type DNS-Default.cmd @echo off netsh interface ipv4 delete dnsservers "Local Area Connection" all netsh interface ipv4 show dnsservers D:\T>
To run one of these scripts, open a Command Prompt with administrative privileges (right-click "Command Prompt" on the Start Menu and pick "Run as administrator") and run the appropriate script. It'll tell you a bit about what it has done and the changes will take effect immediately (i.e., there's no need to reboot). (For what it's worth, the underlying configuration changes made by the two methods I've discussed should be exactly the same, so you could even use the command-line to enable a custom DNS and then use the UI to disable it!)
Personally, I tend to make this switch just before I start working with the Windows Phone emulator - and then switch back to the default configuration when I'm done. Theoretically, I could run my development machine with a custom DNS all the time, but then it wouldn't be configured the same as the rest of the household and I worry that could trip me up some day long after I've forgotten about any of this. But personal issues aside, I encourage people to switch as often or rarely as they'd like. :)
The Benefits
Returning to the IE home page example from earlier, let's take a new trace after configuring a custom DNS:
2.7696164 DNS:QueryId = 0x4E64, QUERY (Standard query), Query for go.microsoft.com of type AAAA on class Internet 2.8465201 DNS:QueryId = 0x4E64, QUERY (Standard query), Response - Success, 2.8590985 DNS:QueryId = 0xE75, QUERY (Standard query), Query for go.microsoft.com of type Host Addr on class Internet 2.8678965 DNS:QueryId = 0x64D2, QUERY (Standard query), Query for go.microsoft.com of type AAAA on class Internet 2.9341095 DNS:QueryId = 0xE75, QUERY (Standard query), Response - Success, 54, 0 2.9441158 DNS:QueryId = 0x64D2, QUERY (Standard query), Response - Success, 3.0086106 HTTP:Request, GET /fwlink/, Query:LinkID=192163
Yup, that's looking much better! To be fair, it's not clear to me why there's a duplicate query for the IPv6 address, but even with three queries (vs. two), it still manages to get the initial HTTP request sent in less than a quarter of a second. Stepping back and looking at the time taken by the entire transaction, it now completes in just under 5 seconds - less than 3% of the original time!
With a custom DNS in place, my frustration developing network applications for Windows Phone 7 went away and I was able to spend my time coding instead of waiting. I'm guessing the underlying DNS server problem that causes these long delays isn't super pervasive (or else they would have fixed the Windows Phone 7 emulator and/or Windows Phone OS by now), but I've also got to imagine I'm not the only one who's affected by this problem. So if you've ever wondered what in the world is taking so long, maybe your DNS server is the problem - and now you know how to fix it! :)