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)
3: Guid key;
5: if (!IsolatedStorageSettings.ApplicationSettings.Contains("InstanceKey"))
7: key = Guid.NewGuid();
8: IsolatedStorageSettings.ApplicationSettings["InstanceKey"] = key;
12: key = (Guid)IsolatedStorageSettings.ApplicationSettings["InstanceKey"];
14: this.RootVisual = new MainPage(key.ToString());
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)
3: IPollingServiceClient client =
6: PollingMonitor.Current.Register(client, sessionId);
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)
7: var found = (from c in this
8: where c.SessionId == sessionId
9: select c).SingleOrDefault();
11: if (found != null)
14: this.Add(new PollingClient(client, sessionId));
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.