ОК, я наконец нашел решение.Это своего рода грязное исправление, но оно работает и стабильно, так что я буду этим пользоваться.
Во-первых, я хочу прояснить саму ситуацию.Я думал, что это тупик, но это не так.Это было на самом деле сочетание двух разных проблем, которые заставили меня думать, что все клиенты ждут, пока сервер застрянет на чем-то.Сервер не застрял, он был просто в середине очень длительного процесса.Дело в том, что у клиента IE была своя собственная проблема, из-за чего казалось, что он ждет вечно.
Мне в итоге удалось выделить 2 проблемы, а затем дать каждой проблеме свое решение.
Проблема № 1: Сервер долго зависает при попытке отправить уведомление отключенному клиенту.
Поскольку это было сделано в цикле,другие клиенты также должны были ждать:
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);
}
}
Итак, чтобы решить эту проблему, я заставил цикл запустить отдельный поток для каждого клиента, чтобы уведомления для клиентов стали независимыми.Вот окончательный код:
[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);
}
}
}
Обратите внимание, что существует один поток для обработки каждого уведомления клиента.Поток также отбрасывает клиента, если ему не удалось отправить уведомление.
Проблема № 2: Клиент разрывает соединение через 10 секунд ожидания.
Эта проблема,Удивительно, но это произошло только в проводнике ... Я не могу это объяснить, но, проведя некоторое исследование в Google, я обнаружил, что я не единственный, кто это заметил, но не смог найти никакого чистого решения, кроме очевидного - "простопинговать сервер каждые 9 секунд ".Именно это я и сделал.
Поэтому я расширил интерфейс контракта, включив в него метод Ping сервера, который мгновенно вызывает метод Pong клиента:
[OperationContract(IsOneWay = true)]
public void Ping()
{
IChatNotification cli = OperationContext.Current.GetCallbackChannel<IChatNotification>();
cli.Pong();
}
обработчик события Pong клиента создаетпоток, который спит в течение 9 секунд, а затем снова вызывает метод ping:
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();
}
И это все.Я протестировал его с несколькими клиентами, удалил некоторых клиентов, закрыв их браузеры, затем снова запустил их, и все заработало.И потерянные клиенты в конечном итоге были очищены сервером.
Еще одно замечание - поскольку клиентское соединение оказалось ненадежным, я окружил его исключением try-catch, чтобы я мог реагировать на случаи, когда соединение самопроизвольно умирает:
try
{
this.client.PublishAsync(this.MyMessage.Text);
this.MyMessage.Text = "";
}
catch
{
this.Messages.Text += "Was disconnected!";
this.client = null;
}
Это, конечно, не помогает, поскольку «PublishAsync» возвращает мгновенно и успешно, в то время как код, который был автоматически сгенерирован (в Reference.cs), выполняет фактическую работу по отправкесообщение на сервер, в другой теме.Единственный способ поймать это исключение - обновить автоматически сгенерированный прокси ... что очень плохая идея ... но я не смог найти другого пути.(Идеи будут оценены).
Вот и все.Если кто-нибудь знает, как проще обойти эту проблему, я буду очень рад услышать.
Ура,
Коби