Silverlight & XAML Playground
about XAML and other Amenities

Handling faults on PollingDuplex while server is not available

2009-09-25T15:57:34+01:00 by Andrea Boschin

During the development of the project I'm currently working I've implemented a polling duplex client and I found some improvements to the code I've published some weeks ago in this post.

The problem I would like to answer here is caused by server unavailability that may origin in many causes. In my scenario I'm using a WCF service hosted by Internet Information Server. While I'm using a server-side thread running in the context of the Application Pool there may be some conditions where Application Pool is recycled so the server thread is aborted and the PollingDuplex communication stops.

There is many reasons why we have to avoid this condition. In my case it is important that the client continue receive updates from the polling server without it require a page refresh from the user. So I've implemented a fault handling procedure that let my client detect when the server become unavailable and then it retry connecting and restart server side thread.

First of all we have to write a registration procedure. My duplex channel work using a Register() ServiceOperation called by the client that subscribe the client in the server thread for updates notification. The client callback contract has a single NotifyAlarm() method used by the server to notify messages to client. Here is the client-side code I use to start the polling:

   1: /// <summary>
   2: /// Registers this instance.
   3: /// </summary>
   4: private void Register()
   5: {
   6:     this.Client = new PollingServiceClient(
   7:         new CustomBinding(
   8:             new PollingDuplexBindingElement
   9:                 {
  10:                     InactivityTimeout = new TimeSpan(1, 0, 0),
  11:                     ClientPollTimeout = new TimeSpan(0, 1, 0)
  12:                 },
  13:             new BinaryMessageEncodingBindingElement(),
  14:             new HttpTransportBindingElement()),
  15:         this.EndPoint);
  16:  
  17:     this.Client.NotifyAlarmReceived += 
  18:         new EventHandler<NotifyAlarmReceivedEventArgs>(Client_NotifyAlarmReceived);
  19:     this.Client.InnerChannel.Faulted += 
  20:         new EventHandler(Client_Faulted);
  21:     this.Client.RegisterCompleted += 
  22:         new EventHandler<System.ComponentModel.AsyncCompletedEventArgs>(Client_RegisterCompleted);
  23:     this.Client.RegisterAsync(this.SessionId);
  24: }

To handle channel fault I subscribe the Faulted event in the InnerChannel. I cannot find any other way to detect when the polling fail due to server unavailability. If you trace the polling of the client and then simulate the server unavailability, recycling the application pool you will see a 404 error (if you use ClientHttpStack the error will be more specific).

Untitled

When this error occur the InnerChannel.Faulted event is raised so you can restart the polling calling the Register() method again. In the Faulted event handler I simply call the Register method again. 

   1: /// <summary>
   2: /// Handles the Faulted event of the Client control.
   3: /// </summary>
   4: /// <param name="sender">The source of the event.</param>
   5: /// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
   6: void Client_Faulted(object sender, EventArgs e)
   7: {
   8:     Deployment.Current.Dispatcher.BeginInvoke(
   9:         () =>
  10:         {
  11:             this.OnDisconnected();
  12:             this.Register();
  13:         });
  14: }

The better will be starting a Timer to retry connection on a regular timeout to let the client returning online after longer server unavailability. This may be useful also when we start the polling because we can dowload the Silverlight application from a server but then it starts the polling to another server. So I've added some code in the RegisterCompleted event handler and when I get an exception from the Register() method I start a separate thread. It waits for 60 seconds then recall the Register() method again.

   1: void Client_RegisterCompleted(object sender, System.ComponentModel.AsyncCompletedEventArgs e)
   2: {
   3:     if (e.Error == null)
   4:         this.OnConnected();
   5:     else
   6:     {
   7:         this.OnFault(e.Error);
   8:  
   9:         ThreadPool.QueueUserWorkItem(
  10:             (o) =>
  11:             {
  12:                 Thread.Sleep(60000);
  13:  
  14:                 Deployment.Current.Dispatcher.BeginInvoke(
  15:                     () => this.Register());
  16:             });
  17:     }
  18: }

You have to be aware that the Faulted event require you to marshal the context of the thread before updating any UI element. In my code I raise an event of a wrapper object to let UI reflect the temporary disconnection. So I have to use the Dispatcher to marshal the Thread to the UI.

This is all for now. I will write more on this argumet on the next days to handle a different condition.

Categories:   Networking
Actions:   E-mail | del.icio.us | Permalink | Comments (0) | Comment RSSRSS comment feed