Note that there are some explanatory texts on larger screens.

plurals
  1. PO
    primarykey
    data
    text
    <p>OK, I finally found a solution. Its kind of a dirty patch, but it works and its stable, so that's what I'll use.</p> <p>First, I want to clarify the situation itself. I thought this was a deadlock, but it wasn't. It was actually a combination of 2 different problems that made me think that the clients are all waiting while the server is stuck on something. The server was not stuck, it was just in the middle of a very lengthy process. The thing is, that the IE client had a problem of its own, which made it seem like it was waiting forever.</p> <p>I eventually managed to isolate the 2 problems and then gave each problem its own solution. </p> <p><strong>Problem number 1: The server hangs for a long time while trying to send a notification to a client that was disconnected.</strong> </p> <p>Since this was done in a loop, other clients had to wait as well:</p> <pre><code> foreach (IChatNotification channel in this.clients) { try { channel.Notify(message); // if this channel is dead, the next iteration will be delayed } catch { toRemove.Add(channel); } } </code></pre> <p>So, to solve this problem, I made the loop start a distinct thread for each client, so the notifications to the clients become independent. Here is the final code:</p> <pre><code>[OperationContract(IsOneWay = true)] public void Publish(string message) { lock (this.clients) { foreach (IChatNotification channel in this.clients) { Thread t = new Thread(new ParameterizedThreadStart(this.notifyClient)); t.Start(new Notification{ Client = channel, Message = message }); } } } public void notifyClient(Object n) { Notification notif = (Notification)n; try { notif.Client.Notify(notif.Message); } catch { lock (this.clients) { this.clients.Remove(notif.Client); } } } </code></pre> <p>Note that there is one thread to handle each client notification. The thread also discards the client, if it failed to send the notification.</p> <p><strong>Problem number 2: The client kills the connection after 10 idle seconds.</strong> </p> <p>This problem, surprisingly, only happened in explorer... I can't really explain it, but after doing some research in Google I found that I was not the only one to notice it, but could not find any clean solution except the obvious - "just ping the server every 9 seconds". Which is exactly what I did.</p> <p>So I extended the contract interface to include a server Ping method, which instantly calls a client's Pong method:</p> <pre><code>[OperationContract(IsOneWay = true)] public void Ping() { IChatNotification cli = OperationContext.Current.GetCallbackChannel&lt;IChatNotification&gt;(); cli.Pong(); } </code></pre> <p>the client's Pong event handler creates a thread that sleeps for 9 seconds and then calls the ping method again:</p> <pre><code>void client_PongReceived(object sender, System.ComponentModel.AsyncCompletedEventArgs e) { // create a thread that will send ping in 9 seconds Thread t = new Thread(new ThreadStart(this.sendPing)); t.Start(); } void sendPing() { Thread.Sleep(9000); this.client.PingAsync(); } </code></pre> <p>And that was it. I tested it with multiple clients, removed some clients by closing their browsers, then re launched them, it all worked. And the lost clients were eventually cleaned by the server.</p> <p>One more note - Since the client connection proved to be unreliable, I surrounded it with a try - catch exception so I can respond to cases where the connection spontaneously dies:</p> <pre><code> try { this.client.PublishAsync(this.MyMessage.Text); this.MyMessage.Text = ""; } catch { this.Messages.Text += "Was disconnected!"; this.client = null; } </code></pre> <p>This, of course, does not help, since the "PublishAsync" returns instantly, and successfully, while the code that was automatically generated (in Reference.cs) does the actual work of sending the message to the server, in another thread. The only way I could think of to catch this exception is by updating the automatically generated proxy... which is a very bad idea... but I could not find any other way. (Ideas will be appreciated).</p> <p>That's all. If anybody knows of an easier way to work around this issue, I will be more than happy to hear.</p> <p>Cheers, </p> <p>Kobi</p>
    singulars
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    plurals
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    1. VO
      singulars
      1. This table or related slice is empty.
    2. VO
      singulars
      1. This table or related slice is empty.
    3. VO
      singulars
      1. This table or related slice is empty.
 

Querying!

 
Guidance

SQuiL has stopped working due to an internal error.

If you are curious you may find further information in the browser console, which is accessible through the devtools (F12).

Reload