如何正确关闭客户端代理(远程主机强行关闭现有连接)?

时间:2013-12-23 18:51:28

标签: c# .net wcf tcp nettcpbinding

在你将问题读到最后之前,请不要关闭。我已经谷歌搜索了几个小时没有成功。


编辑:现在我确信它与WCF缓存打开TCP连接(连接池)的方式有关。请在问题结束时查看编辑#5。


我基本上有一个使用netTcpBinding配置的WCF服务。即使我正常关闭客户端代理(请参阅下面的代码),服务器也始终记录“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 "An existing connection was forcibly closed by the remote host" after closing client

结论是“不关心它”。

有人可以通过一些引用确认这一点,并解释为什么抛出这个异常?

编辑:

记录异常的跟踪:

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

我试图通过WCF为我建立的TCP连接来监控实际发生的情况。

关闭客户端代理后,我仍然看到与我的服务器打开的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为客户端的每个服务   与。沟通。

所以现在我的问题是:如果这是正常的行为,我该怎么做才能防止我的日志被所有这些例外污染?

3 个答案:

答案 0 :(得分:8)

要解决此错误,只需关闭频道工厂。

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

答案 1 :(得分:2)

修改 好。我能够使用您的代码重现问题。好消息是我偶然发现了重现它的方法。我认为您的代码中没有任何问题。我认为正在记录错误,因为您在VS中的调试器中运行它;当调试器关闭服务时导致错误被记录。

按照以下步骤查看您是否不再收到错误(这对我来说每次都很合适):<​​/ p>

  1. 右键单击服务项目,然后选择Debug&gt;启动新实例
  2. 右键单击控制台应用程序,然后选择Debug&gt;启动新实例
  3. 运行控制台以完成。
  4. 检查您的日志文件。
  5. 使用WCF测试客户端窗口停止服务,而不是调试器停止按钮。
  6. 检查您的日志文件。
  7. 以下是我执行上述步骤的冲击波视频的链接:swf file

    <强>原单 你发布的代码在我的系统上运行得很好。我在任何日志中都没有收到任何错误。顺便说一句,我会完全摆脱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();
            }
        }
    }
    

答案 2 :(得分:0)

我怀疑try块中的return命令导致执行跳过finally块,导致连接保持打开状态,直到客户端关闭导致异常。这可能吗?你确定你的finally块被执行了吗?