Исключение WCF получено при закрытии соединения с используемыми обратными вызовами

Я использую netNamedPipeBinding для выполнения межпроцессного взаимодействия WCF из приложения Windows в службу Windows.

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

Чтобы нарисовать картину моего сценария: моя служба Windows может быть поставлена ​​в очередь для выполнения какой-либо работы в любой момент времени с помощью кнопки, нажатой в приложении Windows, а затем он обращается через netNamedPipeBinding , который является привязкой, поддерживающей обратные вызовы (двусторонняя связь), если вы не знакомы, и инициирует запрос на выполнение этой работы (в данном случае процедура загрузки файла), он также генерирует обратные вызовы (события) каждые несколько секунд, начиная от прогресса файла до скорости передачи и т. д. и т. д. обратно в приложение для Windows, поэтому клиент-серверная интеграция ht; так я получаю информацию о том, что работает в моей службе Windows, обратно в приложение Windows.

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

System.ServiceModel.ProtocolException:
  The channel received an unexpected input message with Action 
  'http://tempuri.org/ITransferServiceContract/TransferSpeedChangedCallback' 
  while closing. You should only close your channel when you are not expecting 
  any more input messages.

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

Теперь об ошибке я получаю сразу после выполнения моего вызова Unsubscribe () , который является вторым последним вызовом перед завершением работы приложения и, как мне кажется, является предпочтительным способом отключения клиента WCF. Все, что отписывается перед закрытием соединения, просто удаляет идентификатор клиента из массива, который хранился локально в службе wcf службы win (поскольку это экземпляр, ОБЩИЙ как служба win, так и приложение Windows, поскольку служба win может выполнять работу в запланированные события само по себе), и после удаления массива идентификаторов клиентов я выполняю то, что, как я надеюсь (чувствую), должно быть чистым отключением.

Результатом этого, помимо получения исключения, является то, что мое приложение зависает, пользовательский интерфейс полностью заблокирован, индикаторы выполнения и все остальное на полпути, со всеми признаками, указывающими на наличие состояния гонки или тупиковой ситуации WCF [вздох], но Сейчас я довольно разбираюсь в потоках, и я думаю, что это относительно изолированная ситуация, и я читаю исключение как есть, я не думаю, что это проблема `` потока '' как таковая, поскольку в ней больше говорится о проблеме раннего отключения, которое затем закручивает все мои нити в хаос, возможно, вызывая блокировку.

Мой подход Unsubscribe () на клиенте выглядит так:

    public void Unsubscribe()
    {
        try
        {
            // Close existing connections
            if (channel != null &&
                channel.State == CommunicationState.Opened)
            {
                proxy.Unsubscribe();
            }
        }
        catch (Exception)
        {
            // This is where we receive the 'System.ServiceModel.ProtocolException'.
        }
        finally
        {
            Dispose();
        }
    }

И мой метод Dispose () , который должен выполнить полное отключение:

    public void Dispose()
    {
        // Dispose object
        if (channel != null)
        {
            try
            {
                // Close existing connections
                Close();
                // Attempt dispose object
                ((IDisposable)channel).Dispose();
            }
            catch (CommunicationException)
            {
                channel.Abort();
            }
            catch (TimeoutException)
            {
                channel.Abort();
            }
            catch (Exception)
            {
                channel.Abort();
                throw;
            }
        }
    }

И служба WCF Подписка () аналог и атрибуты класса (для справки) на сервере службы Windows (здесь нет ничего сложного, и мое исключение происходит на стороне клиента):

    [ServiceBehavior(InstanceContextMode = InstanceContextMode.Single, 
    ConcurrencyMode = ConcurrencyMode.Multiple)] 
    public class TransferService : LoggableBase, ITransferServiceContract
    {
        public void Unsubscribe()
        {
            if (clients.ContainsKey(clientName))
            {
                lock (syncObj)
                {
                    clients.Remove(clientName);
                }
            }

#if DEBUG
            Console.WriteLine(" + {0} disconnected.", clientName);
#endif
        }
        ...
    }

Интерфейс:

[ServiceContract(
    CallbackContract = typeof(ITransferServiceCallbackContract), 
    SessionMode = SessionMode.Required)]
public interface ITransferServiceContract
{
    [OperationContract(IsInitiating = true)]
    bool Subscribe();

    [OperationContract(IsOneWay = true)]
    void Unsubscribe();
    ...
}

Интерфейс контракта обратного вызова, он не делает ничего особенного, просто вызывает события через делегатов и т. Д. Причина, по которой я включил это, состоит в том, чтобы показать вам свои атрибуты. Я уже устранил один набор взаимоблокировок, включив UseSynchronizationContext = false :

[CallbackBehavior(UseSynchronizationContext = false, 
ConcurrencyMode = ConcurrencyMode.Multiple)]
public class TransferServiceCallback : ITransferServiceCallbackContract
{ ... }

Очень надеюсь, что кто-нибудь сможет мне помочь! Большое спасибо =:)

7
задан GONeale 18 September 2010 в 08:22
поделиться