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

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


РЕДАКТИРОВАТЬ: Теперь я убежден, что это связано с тем, как кэши 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();
        }
    }
}

Все отлично работает

Служба вызова

Закрытие клиента

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 "Существующее соединение было принудительно закрыто удаленным хостом" после закрытия клиента

Вывод будет "не волнует об этом".

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

РЕДАКТИРОВАТЬ:

Журнал трассировки исключения:

<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-соединение с моим сервером:

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

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

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

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

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

3 ответа

Решение

Чтобы устранить эту ошибку, просто закройте Channel 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();
        }
    }
}

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

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

  1. Щелкните правой кнопкой мыши проект службы и выберите "Отладка"> "Начать новый экземпляр".
  2. Щелкните правой кнопкой мыши консольное приложение и выберите "Отладка"> "Начать новый экземпляр".
  3. Запустите консоль до завершения.
  4. Проверьте свой файл журнала.
  5. Остановите службу с помощью окна тестового клиента WCF, а не кнопки остановки отладчика.
  6. Проверьте свой файл журнала.

Вот ссылка на видео о том, как я делаю вышеуказанные шаги: swf-файл

Оригинал Код, который вы разместили, прекрасно работает в моей системе. Я не получаю ошибок ни в одном журнале. Кроме того, я бы полностью избавился от блока 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();
        }
    }
}

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

Другие вопросы по тегам