Silverlight Playground
about Silverlight and other Amenities

Handling duplicated connections on Polling Duplex

2009-10-23T00:46:00+01:00 by Andrea Boschin

The last tip I would like to propose is a tecnique to avoid a single client machine connect multiple times to the same polling server. This may be the case when the user starts two or more instances of the browser or open the same page in multiple tabs. In this scenario may be unuseful to have every page connected to the server.

In Silverlight 3.0 there is a way to check when a plugin has been loaded many times. When you try to create a LocalReceiver with the same name from two instances you get an exception because they cannot share the same name. So using a LocalReceiver you can check if another browser is open.

   1: public override void Start()
   2: {
   3:     LocalMessageReceiver receiver = this.CreateReceiver();
   4:  
   5:     if (receiver != null)
   6:     {}  // receiver created: I'm the master...
   7:     else
   8:     {}  // cannot create receiver: I'm a slave...
   9: }
  10:  
  11: private LocalMessageReceiver CreateReceiver()
  12: {
  13:     try
  14:     {
  15:         LocalMessageReceiver receiver = new LocalMessageReceiver("PollingReceiver");
  16:         receiver.Listen();
  17:         return receiver;
  18:     }
  19:     catch(ListenFailedException)
  20:     {
  21:         return null;
  22:     }
  23: }

 

Checking the presence of another instance is only half of the work to do. When you detect you are a slave in the machine you need to avoid connecting - slave does nothing because a master already exists - and probably you have to change the user interface to let the user know that there is another instance running.

In my application I need to check the presence of the master every few seconds to let a slave becoming a master. To do this I've created two classes MasterPollingClientCode  SlavePollingClientCore implementing the same interface IPollingClientCore. The first class connect to the server and the other class simply does nothing.

   1: public override void Start()
   2: {
   3:     LocalMessageReceiver receiver = this.CreateReceiver();
   4:  
   5:     if (receiver != null)
   6:         this.Core = new MasterPollingClientCore(this.SessionId, this.EndPoint, receiver);
   7:     else
   8:         this.Core = new SlavePollingClientCore(this.LocalId, 
                          new LocalMessageSender("PollingSender" + this.LocalId.ToString()));
   9:  
  10:     this.Core.AlarmReceived += new EventHandler<NotifyAlarmReceivedEventArgs>(Core_AlarmReceived);
  11:     this.Core.Connected += new EventHandler(Core_Connected);
  12:     this.Core.Disconnected += new EventHandler(Core_Disconnected);
  13:     this.Core.Fault += new EventHandler<PollingFaultEventArgs>(Core_Fault);
  14:     this.Core.Start();
  15:     base.Start();
  16: }
 

The container class, then starts a thread. This thread try to create a new LocalReceiver and when it is able to get an instance it change the core class from slave to master and connect to the server. This way the client is always connected to the server with alwasy one simple connection:

   1: protected override void ThreadProc()
   2:     {
   3:         Debug.WriteLine("ThreadProc");
   4:  
   5:         while(WaitHandle.WaitTimeout == WaitHandle.WaitAny(this.ExitHandles, 5000))
   6:         {
   7:             if (this.Mode != PollingClientMode.Master)
   8:             {
   9:                 Deployment.Current.Dispatcher.BeginInvoke(
  10:                     ()=> this.TryConvertToMaster());
  11:             }
  12:         }
  13:     }
  14:  
  15:     /// <summary>
  16:     /// Tries the convert to master.
  17:     /// </summary>
  18:     private void TryConvertToMaster()
  19:     {
  20:         Debug.WriteLine("TryConvertToMaster");
  21:  
  22:         LocalMessageReceiver receiver = this.CreateReceiver();
  23:  
  24:         if (receiver != null)
  25:         {
  26:             Debug.WriteLine("created");
  27:  
  28:             // detach all
  29:             this.Core.Stop();
  30:             this.Core.AlarmReceived -= new EventHandler<NotifyAlarmReceivedEventArgs>(Core_AlarmReceived);
  31:             this.Core.Connected -= new EventHandler(Core_Connected);
  32:             this.Core.Disconnected -= new EventHandler(Core_Disconnected);
  33:             this.Core.Fault -= new EventHandler<PollingFaultEventArgs>(Core_Fault);
  34:             // create new core
  35:             this.Core = new MasterPollingClientCore(this.SessionId, this.EndPoint, receiver);
  36:             // attach all
  37:             this.Core.AlarmReceived += new EventHandler<NotifyAlarmReceivedEventArgs>(Core_AlarmReceived);
  38:             this.Core.Connected += new EventHandler(Core_Connected);
  39:             this.Core.Disconnected += new EventHandler(Core_Disconnected);
  40:             this.Core.Fault += new EventHandler<PollingFaultEventArgs>(Core_Fault);
  41:             this.Core.Start();
  42:         }
  43:     }
 
 

If you are brave you can try to implement a communication between Slave and Master using a LocalSender. Using the code I provider this task cannot be too hard.

Good Work

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

How to avoid flooding a PollingDuplex server

2009-10-06T14:37:39+01:00 by Andrea Boschin

Returning on the PollingDuplex argument, today I would like to illustrate a tecnique I used to avoid the flooding in my Polling Duplex service. The problem come from the fact that my server retain a list of connected clients and is unable to detect the disconnection in a short time. After a while the notification from the server to the disconnected client will timeout so the server handle the timeout and remove the client from its list. But if for some reason a client Register itself multiple times the server notification list will grow and this may become a problem causing the service to slow down its response time.

In a recent project I have to address this issue because of the way the polling client works. Imagine having a instant messaging client using PollingDuplex in all the pages of a portal. When the user navigate in the website the polling client will repeatly connect and disconnect from the polling server because the user may change page after short time.

How to generate a persistent ClientID

To handle this problem I need to have a globally unique id identifing the connecting client. If the ID is unique across different connected clients the server can use this ID to search previuos instances of the client in his notification list and if found it discart the old client and replace it with the new one. The solution is pretty simple but it require the ability to generate a very unique id (obviously a Guid) and then persist it across different browser session.

The trick is to use the Isolated Storage. The first time I start the client it generate the Guid and save it in the IsolatedStorageSettings collection. Then it use the guid to connect and register to the polling server.

All the other times the client find the Guid in the IsolatedStorageSettings and avoid to generate another Guid but use the old identifier. Here is the code to generate the guid or take id from the storage:

   1: private void Application_Startup(object sender, StartupEventArgs e)
   2: {
   3:     Guid key;
   4:  
   5:     if (!IsolatedStorageSettings.ApplicationSettings.Contains("InstanceKey"))
   6:     {
   7:         key = Guid.NewGuid();
   8:         IsolatedStorageSettings.ApplicationSettings["InstanceKey"] = key;
   9:         IsolatedStorageSettings.ApplicationSettings.Save();
  10:     }
  11:     else
  12:         key = (Guid)IsolatedStorageSettings.ApplicationSettings["InstanceKey"];
  13:  
  14:     this.RootVisual = new MainPage(key.ToString());
  15: }

I put the code in the Application_Startup method then I pass the key to the MainPage constructor because it must use the guid to connect to the server.

On the server side I've slightly changed the Register method and the Polling thread to handle the new guid. First of all I request the id in the Register method:

   1: public void Register(string sessionId)
   2: {
   3:     IPollingServiceClient client =
   4:         OperationContext.Current.GetCallbackChannel<IPollingServiceClient>();
   5:  
   6:     PollingMonitor.Current.Register(client, sessionId);
   7: }

Then the server checks his notification list to add or change the callback client:

   1: /// <summary>
   2: /// Adds the specified item.
   3: /// </summary>
   4: /// <param name="item">The item.</param>
   5: public void Add(IPollingServiceClient client, string sessionId)
   6: {
   7:     var found = (from c in this
   8:                  where c.SessionId == sessionId
   9:                  select c).SingleOrDefault();
  10:  
  11:     if (found != null)
  12:         found.ChangeClient(client);
  13:     else
  14:         this.Add(new PollingClient(client, sessionId));
  15: }

The ChangeClient() method simply put the new callback client in the object instance representing the client in the list.

There are only two drawbacks in this tecnique. The first is that the user can clear his IsolatedStorage using the Silverlight context menu. This deletes the saved Guid so the next time the client connect to the server it generate another Guid. On the other side someone may discover some privacy concerns on having an id globally identifying the client because this may become a way to track the user activities. You have to be aware of this problem and eventually you can mitigate the problem using a TTL to invalidate the Guid after some hours or days so the client will change the Guid and the tracking become hardest.

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