Поиск дуплекса WCF «TwoWay» Пример подписки + обратного вызова

Вновь выплачивать награду, потому что мне действительно нужно знать, как заставить это работать, или окончательный ответ о том, почему это не будет.

Я добавил альтернативное объяснение проблемы здесь.

У меня чертовски непросто получить двусторонний (IsOneWay = false) клиент-сервер WCF для работы в .Net 3 / 3.5.

После того, как клиент успешно зарегистрировался в службе, служба периодически объявляет () обратный звонок зарегистрированным клиентам. Теперь клиент или сервер зависает до тех пор, пока сервер не отправит SendTimeout, отрегулировано на 2 секунды, проходит. Тогда на стороне сервера есть исключение тайм-аута следующим образом. Только после этого клиентский код клиента немедленно ПОЛУЧИТ ВЫЗОВ МЕТОДА и попытается вернуть значение. К тому времени клиентский сокет будет прерван, и работа WCF завершится неудачно.

Мне кажется, что что-то на клиенте висит в локальной очереди WCF от обработки до истечения времени ожидания сокета, но не достаточно рано, чтобы отменить локальный вызов метода. Но если верить приведенному ниже исключению, сервер пытается отправить операцию на http://schemas.microsoft.com/2005/12/ServiceModel/Addressing/Anonymous (неуместно!) время ожидания Может быть, этот URI является просто «именем» удаленно подключенного клиента, так как WCF знает, что он ссылается на него в целях сообщения об ошибке, и это просто означает, что оно ' Не удается загрузить URI. Я не могу сказать, произошел ли сбой сервера в первую очередь или сначала произошел сбой клиента.

Я попытался добавить трассировку WCF, но не получил больше информации.

Подобный пример кода приведен здесь , но он должно быть, было слишком много, чтобы переварить. Я экспериментировал с изменениями этого кода.

TimeoutException 'This request operation sent to http://schemas.microsoft.com/2005/12/ServiceModel/Addressing/Anonymous did not receive a reply within the configured timeout (00:00:00).  The time allotted to this operation may have been a portion of a longer timeout.  This may be because the service is still processing the operation or because the service was unable to send a reply message.  Please consider increasing the operation timeout (by casting the channel/proxy to IContextChannel and setting the OperationTimeout property) and ensure that the service is able to connect to the client.'

Server stack trace: 
   at System.ServiceModel.Dispatcher.DuplexChannelBinder.SyncDuplexRequest.WaitForReply(TimeSpan timeout)
   at System.ServiceModel.Dispatcher.DuplexChannelBinder.Request(Message message, TimeSpan timeout)
   at System.ServiceModel.Channels.ServiceChannel.Call(String action, Boolean oneway, ProxyOperationRuntime operation, Object[] ins, Object[] outs, TimeSpan timeout)
   at System.ServiceModel.Channels.ServiceChannelProxy.InvokeService(IMethodCallMessage methodCall, ProxyOperationRuntime operation)
   at System.ServiceModel.Channels.ServiceChannelProxy.Invoke(IMessage message)

18
задан Community 23 May 2017 в 12:16
поделиться

2 ответа

Для начала получите копию Программирование служб WCF, если у вас ее еще нет.

Если клиент WinForm или WPF, необходимо использовать [CallbackBehavior(UseSynchronizationContext = false)], так как в противном случае клиент не будет обрабатывать входящее сообщение, пока поток пользовательского интерфейса не войдет в цикл сообщений.

Во-первых, "Дуплексный" канал в WCF не является действительно Дуплексным! Сообщение от

  • клиента к серверу
  • может блокировать сообщение, которое сервер ожидает от клиента
  • (или наоборот)

Поскольку сообщения отправляются только в порядке очереди по одному каналу WCF. Двусторонний WCF-канал НЕ дает вам две очереди входящих сообщений. Результаты, возвращающиеся от "Двустороннего" вызова, просто такие же, как и "вызов" на этом уровне стека WCF. Как только вы поймете это, многие проблемы станут более понятными.

Если клиент - WinForm или WPF, вам может понадобиться использовать [CallbackBehavior(UseSynchronizationContext = false)], так как в противном случае клиент не будет обрабатывать входящее сообщение, пока поток UI не войдет в цикл сообщений.

Некоторые правила, которые я нашел, чтобы помочь избежать тупиковых ситуаций. (Посмотрите на мои вопросы по WCF, чтобы увидеть, с какой болью я столкнулся!)

Сервер никогда не должен обращаться к клиенту по тому же соединению, когда вызов от того же клиента находится в в процессе.

И/или

Клиент никогда не должен обращаться обратно к серверу по тому же соединению, которое используется для "обратного вызова" во время обработки обратного вызова.

В следующий раз, я думаю, я просто буду использовать два контракта (и, следовательно, TCP соединения) один для обратного вызова, а другой для всех запросов клиент->сервер. Или использовать мою собственную систему опроса, так как это доставило мне столько боли.

Извините, у меня сегодня нет времени, чтобы написать пример. В любом случае, большинство примеров работают для того, что пытается сделать пример, но ломаются в реальной жизни по каким-то причинам, связанным с вашим приложением.

Лучший веб-сайт, который я знаю для примеров WCF - веб-сайт Юваля Лоуи.

Вы также можете найти вопросы, которые я задавал о WCF на Stack Overflow, полезными, поскольку у меня были те же проблемы, что и у вас.

Также потратив день или два на чтение всех вопросов и ответов по WCF на Stack Overflow, вы получите хорошее представление о проблемах, которых следует избегать.

27
ответ дан 30 November 2019 в 07:44
поделиться

Предполагая, что клиент является приложением WinForms, вы должны сделать обработку обратного вызова независимой от остальной части приложения, используя предложение Яна плюс делегирование работы, которая будет выполняться в потоке пользовательского интерфейса, если это необходимо. .Например, если сервер хочет уведомить клиента о чем-то, например об изменении текста метки, вы можете сделать следующее:

[CallbackBehavior(UseSynchronizationContext = false)]
internal class ServiceCallback : IServiceCallback
{
    ChangeMainFormLabel(string text)
    {
        frmMain.Instance.BeginInvoke(new Action()(() => frmMain.Instance.lblSomething.Text = text));
    }
}

( Экземпляр - статическое свойство, которое возвращает единственный экземпляр frmMain и lblSomething - это некая метка , которую сервер хотел бы изменить.) Этот метод немедленно вернется и освободит сервер от ожидания пользовательского интерфейса клиента, и пользовательский интерфейс будет обновлен, как только это станет возможным. И, что самое главное, никаких тупиков, потому что никто никого не ждет.

4
ответ дан 30 November 2019 в 07:44
поделиться
Другие вопросы по тегам:

Похожие вопросы: