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

C# 如何正确关闭客户端代理(远程主机强制关闭了现有连接)?,c#,.net,wcf,tcp,nettcpbinding,C#,.net,Wcf,Tcp,Nettcpbinding,在把问题读到最后之前,请不要重复关闭;我已经在谷歌上搜索了几个小时没有成功 编辑:现在我确信这与WCF缓存打开的TCP连接(连接池)的方式有关。请看问题结尾处的编辑5 我基本上有一个WCF服务,它使用netTcpBinding配置。即使我优雅地关闭了客户端代理(请参见下面的代码),服务器也会始终记录“System.Net.Sockets.SocketException(0x80004005):远程主机强制关闭了现有连接” 我已经将问题缩小到我能写的最基本的WCF示例。每次关闭客户端应用程序时

在把问题读到最后之前,请不要重复关闭;我已经在谷歌上搜索了几个小时没有成功


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


我基本上有一个WCF服务,它使用
netTcpBinding
配置。即使我优雅地关闭了客户端代理(请参见下面的代码),服务器也会始终记录“
System.Net.Sockets.SocketException(0x80004005):远程主机强制关闭了现有连接”

我已经将问题缩小到我能写的最基本的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>
非常感谢

编辑2:在IIS中托管服务或在Windows服务中自托管服务时,我遇到了相同的问题

编辑3:这里有一个完整的例子来重现这个问题:

编辑4:

我试图通过WCF为我建立的TCP连接来监控引擎盖下实际发生的事情

关闭客户端代理后,我仍然看到与服务器的TCP连接已打开:

我假设这与要缓存以供将来重用的TCP连接(即连接池)有关,因为打开到服务器的新连接(在第一个客户端代理关闭后)不会创建新的TCP连接。如果我调用
Console.WriteLine(newtest().TestTask().Result)在我的应用程序中两次,我仍然只看到一个打开的TCP连接

我还注意到,如果在关闭客户端通道后等待太长时间,则此连接会因超时而终止

编辑5:好的,我在MSDN上找到了关于连接池的文档:

NetTcpBinding使用基于服务的TCP连接池 主机DNS名称和服务正在侦听的端口号。这 当客户端在上调用不同的服务时效果良好 不同的端口或服务托管在单个进程中并共享 港口。如果单个客户端调用共享一个端口的多个服务 托管在不同进程中,或者托管在客户端WAS/IIS中 侧池可能会导致无法连接到服务a的问题 重新用于服务B,导致引发异常 连接中止,并创建了一个新通道。为了避免这个问题,, 使用CustomBinding并指定不同的 客户端上每个服务的ConnectionPoolSettings.GroupName 与……沟通


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

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

编辑 好啊我可以用你的代码重现这个问题。好消息是我也偶然发现了一种不复制它的方法。我认为您的代码中没有任何问题。我认为正在记录错误,因为您正在VS中的调试器中运行它;当调试器关闭服务导致错误被记录时

按照以下步骤进行操作,看看是否不再出现错误(这对我来说每次都非常有效):

  • 右键单击服务项目并选择调试>启动新实例
  • 右键单击控制台应用程序并选择调试>启动新实例
  • 运行控制台直到完成
  • 检查您的日志文件
  • 使用WCF测试客户端窗口而不是调试器停止按钮停止服务
  • 检查您的日志文件
  • 下面是我做上述步骤的冲击波视频链接:

    最初的 你发布的代码在我的系统上运行良好。我没有收到任何日志中的错误。顺便说一句,我将完全摆脱catch块,因为它除了重新引用异常之外什么也不做。然后我会像下面这样写最后一个块。我认为它使代码更清晰,并向读者传达了这样一种想法:如果抛出异常,您将什么也不做

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

    要解决此错误,只需关闭通道工厂即可

    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();
            }
        }
    }
    
    private异步任务TestTask()
    {
    Service1Client proxy=null;
    尝试
    {
    Console.WriteLine(“呼叫服务”);
    proxy=newservice1client();
    返回wait-proxy.doworksync();
    }
    最后
    {
    if(proxy.State!=System.ServiceModel.CommunicationState.Faulted)
    {
    Console.WriteLine(“关闭客户”);
    proxy.ChannelFactory.Close();
    proxy.Close();
    }
    其他的
    {
    Console.WriteLine(“正在中止客户端”);
    proxy.Abort();
    }
    }
    }
    
    这可能与控制台应用程序中的async/Wait有关吗?我记得在某个地方读到,由于线程问题,async/await不适合控制台应用程序。尝试使用非异步方法执行此操作,看看是否会出现相同的问题。@Tim我现在无法测试,但是的,控制台应用程序和GUI应用程序之间存在差异:线程模型,即线程池与主UI线程。我不认为问题来自这里,因为这是一个使用WPF构建的实际应用程序的简化示例。第一,如果您还没有启用WCF跟踪。第二,我想知道
    是否最终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();
            }
        }
    }
    
    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();
            }
        }
    }