WCF ServiceHost Обратный вызов Нестабильный - PullRequest
0 голосов
/ 30 августа 2018

Задача

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

Окружающая среда WCF:

Сервер: апплет в системном трее

Клиент: настольное приложение WinForms

Ресурсы:

Способ запуска настольного приложения из лотка

// Start the process.
ProcessStartInfo oProcessInfo = new ProcessStartInfo()
{
    FileName = regValue.ToString(),
    WindowStyle = ProcessWindowStyle.Normal,
    UseShellExecute = true,
    CreateNoWindow = false,
};
Process oProcess = new Process()
{
    StartInfo = oProcessInfo,
};
oProcess.Start();

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

var callback = IpcToTray.Callback;
if (null == callback)
    return 0;

UInt16 idIsLaunched = callback.IsAppLaunched();
if (Id_AppIsLaunched == idIsLaunched)
    return true;

В данный момент я использую PerCall, но использую PerSession. И то и другое не помогло.

[ServiceBehavior(InstanceContextMode = InstanceContextMode.PerCall)]
public class IpcToTray : IIpcToTray
{
}

Вот некоторые из ошибок:

System.TimeoutException
  HResult=0x80131505
  Message=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:10).  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.
  Source=mscorlib
  StackTrace:
   at System.Runtime.Remoting.Proxies.RealProxy.HandleReturnMessage(IMessage reqMsg, IMessage retMsg)
   at System.Runtime.Remoting.Proxies.RealProxy.PrivateInvoke(MessageData& msgData, Int32 type)
   at MyAppIpc.IIpcCallbackToTray.GetMyAppMode()
   at MyAppTray.Communication.IpcFromTray.GetMyAppMode() in ...\IpcFromTray.cs:line 194

System.ObjectDisposedException
  HResult=0x80131622
  Message=Cannot access a disposed object.
Object name: 'System.ServiceModel.ServiceHost'.
  Source=System.ServiceModel
  StackTrace:
   at System.ServiceModel.Channels.CommunicationObject.ThrowIfDisposedOrImmutable()
   at System.ServiceModel.Channels.CommunicationObject.Open(TimeSpan timeout)
   at System.ServiceModel.Channels.CommunicationObject.Open()
   at MyAppTray.CustomApplicationContext.StartPipeServer() in ...\MyAppTray\CustomApplicationContext.cs:line 105

System.ServiceModel.CommunicationObjectAbortedException
  HResult=0x80131501
  Message=The operation 'IsAppLaunched' could not be completed because the sessionful channel timed out waiting to receive a message.  To increase the timeout, either set the receiveTimeout property on the binding in your configuration file, or set the ReceiveTimeout property on the Binding directly.
  Source=mscorlib
  StackTrace:
   at System.Runtime.Remoting.Proxies.RealProxy.HandleReturnMessage(IMessage reqMsg, IMessage retMsg)
   at System.Runtime.Remoting.Proxies.RealProxy.PrivateInvoke(MessageData& msgData, Int32 type)
   at MyAppIpc.IIpcCallbackToTray.IsAppLaunched()
   at MyAppTray.Communication.IpcFromTray.IsAppLaunched() in ...\MyAppTray\Communication\IpcFromTray.cs:line 142

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

Я пытаюсь перезапустить после сбоя, но .Close(), за которым следует .Open(), не всегда работает, как может быть утилизировано, создание нового получает уже существующую конечную точку URI. Я пробовал много способов добиться стабильности безрезультатно.

Сейчас у меня время до 10 секунд. Исходное время ожидания составляло 1 минуту, по умолчанию Microsoft. Сначала я думал, что мое приложение зависло. Сессионные вещи не помогают делу. Я попытался добавить задержку Thread.Sleep (4000), но это раздражает и ничего не делает. Приложение может быть открыто, но опять же, спящие 4000 или 4000000000000 не имеют никакого значения.

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

Мысли? Я сейчас очень расстроен.

1 Ответ

0 голосов
/ 30 августа 2018

У меня были две проблемы с моим кодом, который я выкладываю здесь для потомков.

  1. Задержки / DOS

Основная проблема заключалась в том, что я делал обратные вызовы слишком близко (не один или два) друг к другу. Во время отладки я заметил, что сбой был внутри моего do-while цикла. Я отслеживал количество итераций внутри цикла в поисках изменения состояния приложения и заметил, что счетчик равен 1, а не 0. Это дало мне понять, что мне нужна задержка. Сначала я установил задержку на 25 мс между вызовами, но проблема все еще была. Затем я попытался 50 мс, что решило проблему.

Мне пришлось добавить 3-секундную задержку после нескольких разных вызовов, чтобы ссылка не вышла из строя. Я немного читал, и это по замыслу. Microsoft лучше всего понимает, что в WCF действует схема DOS (отказ в обслуживании). Возможно, я столкнулся с этим или просто нужно немного времени для очистки канала, чтобы сбросить его перед другим вызовом, хотя этот аргумент не объясняет более длинный.

  1. Закрытие канала при закрытии приложения

    this.HostIpcToTray.CloseCallback();
    this.IpcPipeFactory.Close();
    
    private void IpcPipeFactory_Closing(object sender, EventArgs e)
    {
        this.IsConnectedPipeToTray = false;
        this.HostIpcToTray = null;
        this.IpcPipeFactory.Closing -= IpcPipeFactory_Closing;
    }
    

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

Мне пришлось отписаться от события Closing, которое я делаю в событии Closing.

Я вызываю канал Close (), чего я не делал. Я предполагаю, что сборка мусора не закрывает каналы должным образом. Я заметил проблему, когда попытался закрыть основное приложение, клиент, а затем снова запустить приложение и использовать трей, чтобы сообщить, что я выбросил ошибку. Небольшое исследование привело к приведенному выше коду.

Многие примеры показывают базовую структуру для WCF IPC, но дьявол кроется в деталях для производственного кода.

...