условия гонки в WCF с использованием не односторонних методов
-
19-08-2019 - |
Вопрос
Я разрабатываю клиент-серверное приложение для чата (на самом деле это не так, но давайте представим, что это так :)), и я немного озадачен некоторыми условиями гонки, с которыми столкнулся.
Допустим, у меня есть следующий код:
public interface IServer
{
[OperationContract(IsOneWay = false)]
[FaultContract(typeof(ChatException))]
void BroadcastMessage(string msg);
}
public class Server : IServer
{
void BroadcastMessage(string msg) // I'm not mentionning the try/catch/throw FaultException here for readability purposes
{
foreach (IClientCallback c in callbacks){
c.ReceiveMessage(msg);
}
}
}
public interface IClientCallback
{
[OperationContract(IsOneWay = true)]
void ReceiveMessage(string s);
}
И вот выдержка из конфигурации привязки :
<endpoint address=""
binding="netTcpBinding"
bindingConfiguration="DuplexBinding"
contract="IServer" />
<binding name="DuplexBinding" sendTimeout="00:01:00">
<reliableSession ordered="true" inactivityTimeout="00:05:00" enabled="true"/>
<security mode="None">
<transport clientCredentialType="Windows" protectionLevel="EncryptAndSign" />
<message clientCredentialType="Windows" />
</security>
</binding>
Это, конечно, какой-то псевдо-c #, я удалил много нерелевантного кода для большей ясности.
Теперь к делу :Этот код не будет работать.Когда я вызываю BroadcastMessage, метод никогда не возвращается, и в конечном итоге я получаю тайм-аут на стороне клиента.Если я отлаживаю на стороне сервера, все кажется нормальным (я возвращаюсь из метода BroadcastMessage точно так, как можно было бы ожидать, и я не блокирую любые односторонние вызовы ReceiveMessage)
Вот два способа исправить этот код :
- удалите FaultContract и объявите метод BroadcastMessage как oneway=true
- Транслируйте сообщение всем, КРОМЕ первоначального отправителя
Мое первое предположение состояло в том, что клиентская сторона ожидала возврата сервера и, следовательно, была недоступна для обработки входящего вызова ReceiveMessage с сервера, тем самым блокируя сервер, НО ReceiveMessage объявлен как oneway, и отладка сервера показывает, что он не блокируется при любом вызове ReceiveMessage
Теперь, мои вопросы :
Что происходит?
Есть ли другие способы исправить это?(может быть, настроив конфигурацию привязки ?)
Допустим, я выбираю исправление 2 (т. Е. не отправляю ответную трансляцию отправителю), что произойдет, если сервер вызовет мой обратный вызов ReceiveMessage (потому что кто-то другой отправил мне сообщение), пока я жду завершения моего собственного вызова BroadcastMessage?
Я читал, что односторонние вызовы не являются полностью односторонними, и что сервер все еще ожидает HTTP-ответа от другой стороны.Есть какие-нибудь подробности по этому поводу?в частности, может ли клиент отвечать на такие http-ответы, когда он заблокирован при удаленном вызове?
Редактировать:Консоль .net 3.5 на стороне сервера, Winforms .net 3.5 на стороне клиента
Решение
Это звучит как тупик, возможно, из-за контекста синхронизации.Что такое клиент?Winform?WPF?WCF уважает sync-context, что означает "переключиться на поток пользовательского интерфейса" для winforms и WPF.Если вы создание запрос на блокировку в потоке пользовательского интерфейса, затем игра окончена.
Попробуйте выполнить свой запрос WCF в фоновом потоке, чтобы поток пользовательского интерфейса был доступен для обслуживания входящего запроса;это может быть так же просто, как использование ThreadPool
, или , может быть BackgroundWorker
.
Другие советы
попробуйте добавить этот атрибут в вашу конкретную реализацию вашего сервиса:
[System.ServiceModel.ServiceBehavior(UseSynchronizationContext=false)]
public class Server : IServer {}
Во-вторых, попробуйте включить трассировку WCF, чтобы увидеть, происходит ли у вас что-то в недрах вашей реализации WCF, что доставляет вам огорчение.У меня было несколько действительно странных ошибок, которые имели смысл только тогда, когда я начал отслеживать и обнаружил ФАКТИЧЕСКОЕ сообщение об ошибке, которое возникало.