Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
473 views
in Technique[技术] by (71.8m points)

c# - How to properly close a client proxy (An existing connection was forcibly closed by the remote host)?

Please don't close as duplicate until you read the question to the end; I already googled for hours without success.


EDIT: Now I'm convinced it's related to the way WCF caches opened TCP connections (connection pooling). Please take a look at edit #5 at the end of he question.


I basically have a WCF service that uses a netTcpBinding configuration. Even if I close the client proxy gracefully (see code below), server always logs "System.Net.Sockets.SocketException (0x80004005): An existing connection was forcibly closed by the remote host".

I've narrowed down the issue to the most basic WCF example I could write. I'm getting the exception in the logs produced by WCF tracing every-time I close the client application. I'm not getting any exception in my own code though, which means it works as expected and I can't debug anything to see what's going wrong for WCF to add an error in my logs.

Service interface/implementation:

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

Server-side configuration:

<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>

Client-side 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>

Client-side code that consumes the service (VS2012 generated the client proxy for me using "Add service reference"):

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();
        }
    }
}

Everything works fine:

Calling service

Closing client

12

But as soon as the application terminates, the server logs an exception. I understand I shouldn't be worried about this exception because it works as expected (exception only appears in logs) and could happen anyway in case the client is terminated abruptly before calling .Close()/.Abort().

But still, is this a normal behavior? I mean, if I correctly closes my client proxy, I expect the server to not log an exception (that is polluting my logs). I also assume some TCP connection is still established between the client and the server (unknown state) after closing the client proxy, because the server only logs the exception after the whole client application terminates. If such a connection is still opened, can't this introduce unexpected behavior (such as max number of connected clients)? Is this really expected?

I found different threads about the issue:

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 "An existing connection was forcibly closed by the remote host" after closing client

The conclusion would be "don't care about it".

Could someone confirm that with some references and explain why this exception is thrown anyway?

EDIT:

Log trace of the exception:

<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>

Thanks a lot

EDIT 2: I have the same problem when hosting the service in IIS or when it's self-hosted in a Windows service

EDIT 3: here's a full example to reproduce the issue: http://speedy.sh/ENB59/wcf-test.zip

EDIT 4:

I tried to monitor what is actually happening under the hood with the TCP connection WCF establishes for me.

After closing the client proxy, I still see an opened TCP connection to my server:

enter image description here

I assume this is related to the TCP connection to be cached for a future re-use (i.e. connection pooling), because opening a new connection to the server (after the first client proxy has been closed) doesn't create a new TCP connection. If I call Console.WriteLine(new Test().TestTask().Result); twice in my application, I still only see one opened TCP connection.

I also noted this connection dies because of a timeout if I wait too long after closing the client channel.

EDIT 5: OK, I found documentation on MSDN about that connection pooling:

The NetTcpBinding uses TCP connection pooling based on the service’s host DNS name and the port number the service is listening on. This works well when a client makes calls to different services on different ports, or services are hosted in a single process and share a port. If a single client calls multiple services sharing a port that are hosted in different processes, or are WAS/IIS hosted, the client side pooling may lead to problems where a connection to Service A is reused for Service B, resulting in an exception being thrown, the connection aborted, and a new channel created. To avoid this problem, use a CustomBinding and specify a different ConnectionPoolSettings.GroupName for each service the client communicates with.

So now my question would be: if that's a normal behavior, what could I do to prevent my log to be polluted with all those exceptions?

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

To resolve this error, simply close the Channel Factory as well.

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();
        }
    }
}

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...