The blog of dlaa.me

Posts from October 2010

"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:

  1. 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.

  2. 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.

Step 0 Step 1 Step 2 Step 3

 

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! :)

Is that a BLOB in your pocket, or are you just happy to see me? [Silverlight REST-based Azure/S3 BLOB API updated to run on Windows Phone 7!]

It was about five months ago that I blogged about BlobStore, a simple Silverlight 4 REST-based cloud-oriented file management app for Azure and S3. I pitched it like this:

It's a small, lightweight Silverlight 4 application that acts as a basic front-end for the Windows Azure Simple Data Storage and the Amazon Simple Storage Service (S3)! Just run the BlobStore app, point it at a properly provisioned Azure or S3 account (see below for details on provisioning), and all of a sudden sharing files with yourself and others becomes as simple as dragging and dropping them!

...

Included with every code download is a free REST wrapper for basic Azure and S3 blob access that handles all the tricky Authorization header details for you. It's almost guaranteed to make your next Azure/S3 application a snap to develop and a wild success in the marketplace.

BlobStore with silly sample data

Disclaimer: None of the file names above is accurate. :)

 

I know people have incorporated my REST API wrapper code (BlobStoreApi.cs) into their Silverlight projects, so it wasn't too surprising when I heard that some of you want the same functionality for your Windows Phone 7 applications. While you might expect the existing code to work as-is, there's a catch: Windows Phone 7's version of the Silverlight framework doesn't support the HttpWebRequest.AllowWriteStreamBuffering or WebRequest.ContentLength properties - and both are used by the original BlobStoreApi.cs implementation...

Fortunately, the lack of AllowWriteStreamBuffering isn't a big deal because it's used in only one place and can be commented-out with no loss of functionality (assuming the default behavior of buffering the entire upload doesn't bother you). However, the lack of ContentLength is a little more problematic because its use is more tightly integrated with the rest of code. So it seemed worthwhile for me to update BlobStoreApi.cs for Windows Phone 7 - and it turned out to be fairly simple to remove ContentLength for Windows Phone 7 without changing the existing behavior for Silverlight 4 and desktop .NET.

Because the original file management sample application didn't translate very well to the phone, I dashed off a quick "note taking" sample instead:

BlobStoreOnWindowsPhone sample

The way the new sample works is that the user can type short notes in a text box and then hit the "add" button to upload them to their pre-configured Azure/S3 account as BLOBs with the blob name being the current DateTime.Now.Ticks value and its content being the text of the note. All available notes are listed by date of creation; their text contents are downloaded on demand and shown to the user when selected. Any note can be deleted by tapping it and hitting the "delete" button.

It's a simple application, but it shows off the list/get/put/delete functionality that BlobStoreApi implements for Azure, S3, and IsolatedStorage (the last existing mainly for test purposes). During calls to the BLOB service, the sample application shows a simple progress screen informing the user of the ongoing transaction. (Of course, IsolatedStorage is very fast, so the progress screen is little more than a flicker by default - it's more meaningful for Azure/S3 web access.)

The complete implementation can be found in BlobStore\BlobStoreApi.cs and compiles/runs on the .NET, Silverlight, and Windows Phone 7 platforms. I encourage readers to use this code to create way more compelling applications than my samples! :)

 

[Click here to download the complete BlobStore source code and sample applications for both Silverlight 4 and Windows Phone 7.]

 

Note: By default, the Windows Phone sample uses IsolatedStorage because I don't want to publish the keys to my Azure/S3 accounts. :) If you want to try it with your own account(s), just open MainPage.xaml.cs, uncomment the relevant line below, and add the missing information:
// TODO: Switch to AzureBlobStoreClient or S3BlobStoreClient after entering your connection information
private readonly BlobStoreClient _client = new IsolatedStorageBlobStoreClient();
//private readonly BlobStoreClient _client = new AzureBlobStoreClient("Account Name", "Access Key");
//private readonly BlobStoreClient _client = new S3BlobStoreClient("Access Key ID", "Secret Access Key", "Bucket Name");
Aside: Don't forget to provision your account first; see the notes here for more information!

There's no substitute for customer feedback! [Improving Windows Phone 7 application performance now a bit easier with LowProfileImageLoader and DeferredLoadListBox updates]

PhonePerformance List Scrolling sample Windows Phone 7 applications run on hardware that's considerably less powerful than what drives typical desktop and laptop machines. Therefore, tuning phone applications for optimum performance is an important task - and a challenging one! To help other developers, I previously coded and blogged about two classes: LowProfileImageLoader (pushes much of the cost of loading images off the UI thread) and DeferredLoadListBox (improves the scrolling experience for long lists). These two classes can be used individually or together and have become a regular part of the recommendations for developers experiencing performance issues.

 

I've heard from lots of people who've benefitted from LowProfileImageLoader and DeferredLoadListBox - thank you! I've also received some great suggestions; I recently implemented some of them and wanted to share the new code/bits:

  • The first issue was reported to me by Corrado Cavalli via email. Whereas my sample applications loaded data after they'd initialized, Corrado's DeferredLoadListBox scenario populated its data during initialization. This led to code running in OnApplyTemplate which made the (wrong) assumption that the ListBox's ListBoxItem containers would already have been created. That's not the case during OnApplyTemplate, so DeferredLoadListBox got confused and threw an exception. The simple fix for this problem is to allow for missing containers during OnApplyTemplate - which I've done. Fortunately, subsequent activity (container creation during PrepareContainerForItemOverride) already ensures the right things happen after the containers are created.

  • The second issue was reported to me by Pat Long in the comments to my first blog post. The problem he encountered was that Blend would crash when working with an application using LowProfileImageLoader in certain cases (presumably because of the background worker thread it creates). There's an easy fix here as well - simply short-circuit the worker thread at design-time because the performance implications of LowProfileImageLoader aren't relevant then anyway. I reproduced the problem myself with Visual Studio and fixed it there - then I checked Pat's blog and found he'd done almost exactly the same thing for his Blend-based scenario! [It's nice when separate efforts arrive at the same place. :) ]

  • The third suggestion also came from comments to that blog post - David Burela wanted to use LowProfileImageLoader for images that were part of his application. I'd only been considering the cost of downloading images when I wrote this class, but David pointed out that much of the same "UI thread-friendly" logic should apply to local images as well. Fortunately, it was a simple matter to add support for relative Uris (ex: "/Images/Picture.jpg") - and now LowProfileImageLoader works well with both kinds of images!

    Note: For this new functionality to work, the project's images must have their "Build Action" set to "Content" (not "Resource"). (Here's what that setting looks like in Visual Studio.)
  • The fourth suggestion came from the comments to my second blog post where Johnny Westlake pointed out that LowProfileImageLoader could be counterproductive once it had already worked its magic. In other words, though it's great for lowering the cost of initial image load, the throttling behavior might be undesirable during subsequent loads (ex: during Back button navigation) because by that time the image has been cached by the web stack and the download cost is much lower. At first, I wasn't sure how LowProfileImageLoader could tell which situation a particular image was in - then I realized I didn't have to! :)

    What I've done instead is add a static LowProfileImageLoader.IsEnabled property (true by default) that the developer can toggle whenever he/she wants to (ex: on Back navigation to a page that has already used LowProfileImageLoader). When this property is set to false, LowProfileImageLoader doesn't perform its UI thread sleight-of-hand so the image load behavior is the same as it would have been if LowProfileImageLoader weren't present at all. While this might seem like a bit of a pass the buck move on my part, the practical truth is that the application's author has way more context than LowProfileImageLoader does. So in lieu of a reliable way to detect the cache state of individual images (I'm open to suggestions, by the way!), the new IsEnabled property seems like a reasonable "next best thing".

 

[Click here to download the compiled PhonePerformance assembly, sample applications, and full source code for everything.]

 

My thanks go out to the people who have shared their experience and suggestions with/for LowProfileImageLoader and DeferredLoadListBox! While these two classes are not appropriate in every situation, they seem to be useful in enough situations that it's worth giving them a quick try if you're experiencing relevant performance problems. Both classes are easy to hook up, so trying them out takes no more than a couple of minutes - which will be time well spent if the problem goes away!

I wish everyone good luck with their Windows Phone 7 applications - and please don't forget the value of great performance! :)