Как правильно закрыть клиентский прокси (существующее соединение было принудительно закрыто удаленным хостом)? - программирование

Как правильно закрыть клиентский прокси (существующее соединение было принудительно закрыто удаленным хостом)?

Пожалуйста, не закрывайте дубликаты, пока вы не прочитаете вопрос до конца; Я уже много часов работал без работы.


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


У меня в основном есть служба WCF, которая использует конфигурацию netTcpBinding. Даже если я закрываю клиентский прокси изящно (см. Код ниже), сервер всегда регистрирует "System.Net.Sockets.SocketException (0x80004005): An existing connection was forcibly closed by the remote host".

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

Сервисный интерфейс/реализация:

[ServiceContract]
public interface IService1
{
    [OperationContract]
    string DoWork();
}
...
public class Service1 : IService1
{
    public string DoWork()
    {
        return "12";
    }
}

Конфигурация на стороне сервера:

<configuration>
  <system.web>
    <compilation debug="true" targetFramework="4.5" />
    <httpRuntime targetFramework="4.5" />
  </system.web>
  <system.serviceModel>
    <behaviors>
      <serviceBehaviors>
        <behavior name="">
          <serviceMetadata httpGetEnabled="true" httpsGetEnabled="true" />
          <serviceDebug includeExceptionDetailInFaults="true" />
        </behavior>
      </serviceBehaviors>
    </behaviors>
    <serviceHostingEnvironment aspNetCompatibilityEnabled="true" multipleSiteBindingsEnabled="true" />
    <services>
      <service name="WebApplication1.Service1">
        <endpoint address="" binding="netTcpBinding" bindingConfiguration="netTcpEndpointBinding" contract="WebApplication1.IService1" />
      </service>
    </services>
    <bindings>
      <netTcpBinding>
        <binding name="netTcpEndpointBinding">
          <security mode="None" />
        </binding>
      </netTcpBinding>
    </bindings>
  </system.serviceModel>
</configuration>

Конфигурация на стороне клиента:

<configuration>
  <system.serviceModel>
    <bindings>
      <netTcpBinding>
        <binding name="NetTcpBinding_IService1">
          <security mode="None" />
        </binding>
      </netTcpBinding>
    </bindings>
    <client>
      <endpoint address="net.tcp://localhost/WebApplication1/Service1.svc"
        binding="netTcpBinding" bindingConfiguration="NetTcpBinding_IService1"
        contract="ServiceReference1.IService1" name="NetTcpBinding_IService1" />
    </client>
  </system.serviceModel>
</configuration>

Клиентский код, который использует эту услугу (VS2012 сгенерировал прокси-сервер клиента для меня, используя "Добавить ссылку на службу" ):

private async Task<string> TestTask()
{
    Service1Client proxy = null;

    try
    {
        Console.WriteLine("Calling service");

        proxy = new Service1Client();
        return await proxy.DoWorkAsync();
    }
    finally
    {
        if (proxy.State != System.ServiceModel.CommunicationState.Faulted)
        {
            Console.WriteLine("Closing client");
            proxy.Close();
        }
        else
        {
            Console.WriteLine("Aborting client");
            proxy.Abort();
        }
    }
}

Все работает нормально:

Calling service

Закрывающий клиент

12

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

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

Я нашел разные темы по этой проблеме:

http://social.msdn.microsoft.com/Forums/vstudio/en-US/0f548f9b-7051-46eb-a515-9185f504d605/error-using-nettcpbinding-an-existing-connection-was-forcibly-closed-by-the-remote-host?forum=wcf

wcf "существующее соединение было принудительно закрыто удаленным хостом" после закрытия клиента

Вывод будет "не заботится об этом".

Может ли кто-нибудь подтвердить это с некоторыми ссылками и объяснить, почему это исключение все равно?

EDIT:

Трассировка журнала исключения:

<Exception>
<ExceptionType>System.Net.Sockets.SocketException, System, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</ExceptionType>
<Message>An existing connection was forcibly closed by the remote host</Message>
<StackTrace>
à System.ServiceModel.Channels.SocketConnection.HandleReceiveAsyncCompleted()
à System.ServiceModel.Channels.SocketConnection.OnReceiveAsync(Object sender, SocketAsyncEventArgs eventArgs)
à System.Net.Sockets.SocketAsyncEventArgs.FinishOperationAsyncFailure(SocketError socketError, Int32 bytesTransferred, SocketFlags flags)
à System.Net.Sockets.SocketAsyncEventArgs.CompletionPortCallback(UInt32 errorCode, UInt32 numBytes, NativeOverlapped* nativeOverlapped)
à System.Threading._IOCompletionCallback.PerformIOCompletionCallback(UInt32 errorCode, UInt32 numBytes, NativeOverlapped* pOVERLAP)
</StackTrace>
<ExceptionString>System.Net.Sockets.SocketException (0x80004005): An existing connection was forcibly closed by the remote host</ExceptionString>
<NativeErrorCode>2746</NativeErrorCode>
</Exception>

Спасибо большое

ИЗМЕНИТЬ 2: У меня такая же проблема при размещении службы в IIS или при ее самообслуживании в службе Windows

ИЗМЕНИТЬ 3: здесь полный пример для воспроизведения проблемы: http://speedy.sh/ENB59/wcf-test.zip

ИЗМЕНИТЬ 4:

Я попытался контролировать то, что на самом деле происходит под капотом, с TCP-соединением WCF для меня.

После закрытия прокси-сервера клиента я все еще вижу открытое TCP-соединение с моим сервером:

enter image description here

Я предполагаю, что это связано с кэшированием TCP-соединения для последующего повторного использования (например, пула соединений), поскольку открытие нового подключения к серверу (после того, как первый клиентский прокси был закрыт) не создает новое TCP-соединение. Если я дважды вызываю Console.WriteLine(new Test().TestTask().Result); в моем приложении, я вижу только одно открытое TCP-соединение.

Я также отметил, что это соединение умирает из-за таймаута, если я слишком долго жду закрытия клиентского канала.

РЕДАКТИРОВАТЬ 5: ОК, я нашел документацию по MSDN об этом пуле соединений:

NetTcpBinding использует пул соединений TCP на основе сервисов DNS-имя хоста и номер порта, который прослушивает служба. Эта хорошо работает, когда клиент совершает звонки на различные услуги по разные порты или службы размещаются в одном процессе и совместно используются порт. Если один клиент вызывает несколько служб, разделяющих порт, который размещаются в разных процессах или размещаются WAS/IIS, клиент боковое объединение может привести к проблемам, когда соединение с сервисом A повторно используется для обслуживания B, в результате чего создается исключение, соединение прервано, и создан новый канал. Чтобы избежать этой проблемы, используйте CustomBinding и укажите другое ConnectionPoolSettings.GroupName для каждой службы клиент общается с.

Итак, теперь мой вопрос: если это нормальное поведение, что я могу сделать, чтобы предотвратить загрязнение моего журнала всеми этими исключениями?

4b9b3361

Ответ 1

Чтобы устранить эту ошибку, просто закройте канал Factory.

private async Task<string> TestTask()
{
    Service1Client proxy = null;

    try
    {
        Console.WriteLine("Calling service");

        proxy = new Service1Client();
        return await proxy.DoWorkAsync();
    }
    finally
    {
        if (proxy.State != System.ServiceModel.CommunicationState.Faulted)
        {
            Console.WriteLine("Closing client");
            proxy.ChannelFactory.Close();
            proxy.Close();
        }
        else
        {
            Console.WriteLine("Aborting client");
            proxy.Abort();
        }
    }
}

Ответ 2

Edit ОК. Я смог воспроизвести проблему, используя ваш код. Хорошей новостью является то, что я также наткнулся на способ не воспроизвести ее. Я не думаю, что у вас есть проблемы с вашим кодом. Я думаю, что ошибка регистрируется, потому что вы запускаете ее в отладчике в VS; и когда отладчик, отключивший службу, вызывает ошибку для регистрации.

Выполните следующие действия и убедитесь, что вы больше не получаете ошибку (это отлично работает для меня каждый раз):

  • Щелкните правой кнопкой мыши проект службы и выберите "Отладка" > "Запустить новый экземпляр"
  • Щелкните правой кнопкой мыши консольное приложение и выберите "Отладка" > "Запустить новый экземпляр"
  • Запустите консоль до завершения.
  • Проверьте файл журнала.
  • Остановить службу, используя окно тестового клиента WCF, а не кнопку остановки отладки.
  • Проверьте файл журнала.

Здесь ссылка на видео с ударной волной, в которой я делаю следующие шаги: swf file

Orginal Код, который вы опубликовали, отлично работает в моей системе. Я не получаю ошибок в каких-либо журналах. В стороне, я бы полностью избавился от блока catch, так как он ничего не делает, кроме повторного исключения. Затем я напишу блок finally, как показано ниже. Я думаю, что это делает код более чистым и передает идею читателю, что вы ничего не делаете, если выбрано исключение.

Service1Client proxy = null;

try
{
    Console.WriteLine("Calling service");
    proxy = new Service1Client();
    return await proxy.DoWorkAsync();
}
finally
{
    if (proxy != null)
    {
        if (proxy.State == CommunicationState.Faulted)
        {
            Console.WriteLine("Aborting client");
            proxy.Abort();
        }
        else
        {
            Console.WriteLine("Closing client");
            proxy.Close();
        }
    }
}

Ответ 3

Я подозреваю, что ваша команда возврата в блоке try заставляет выполнение пропускать блок finally, заставляя ваше соединение оставаться открытым до тех пор, пока выключение клиента не приведет к исключению. Это возможно? Вы убедились, что ваш блок finally выполнен?