当 Disposed 时,WCF 的 ClientBase<TChannel> 如何处理 TCP 连接?

Dua*_*nne 5 .net c# sockets wcf tcp

不久前,我看到一篇有趣的文章,解释说HttpClient当代码执行但不关闭 TCP 套接字时,放入using 块将处理对象,并且 TCP 状态最终将转到TIME_WAIT并保持该状态列表以进行进一步的活动4 分钟(默认)。

所以基本上多次使用这个:

using(var client = new HttpClient())
{
    //do something with http client
}
Run Code Online (Sandbox Code Playgroud)

导致许多打开的 TCP 连接处于TIME_WAIT 中

你可以在这里阅读全文:

您使用 HttpClient 是错误的,它会破坏您的软件的稳定性

所以我想知道如果我对ClientBase<TChannel>Visual Studio 创建的派生服务类做同样的事情会发生什么,当你右键单击一个项目并选择添加添加服务引用时。. 并实现了这一点:

    //SomeServiceOutThere inherits from ClientBase
    using (var service = new SomeServiceOutThere()) 
    {
       var serviceRequestParameter = txtInputBox.Text;
       var result = service.BaddaBingMethod(serviceRequestParameter);
       //do some magic (see Fred Brooks quote)
    }
Run Code Online (Sandbox Code Playgroud)

但是,我无法重新创建完全相同的行为,我想知道为什么。

  1. 我创建了一个小型桌面应用程序并添加了对 IIS 托管的 WCF 服务的引用。
  2. 接下来我添加了一个按钮,它基本上通过 using 代码块调用代码,就像我上面展示的那样。
  3. 第一次访问服务后,我为 IP 运行 netsat,结果如下:

在此处输入图片说明

  1. 到现在为止还挺好。我再次单击按钮,果然,新连接建立,而第一个进入TIME_WAIT状态:

在此处输入图片说明

  1. 但是,在此之后,每次我点击服务时,它都会使用ESTABLISHED连接,并且不再像HttpClient演示中那样打开(即使将不同的参数传递给服务,但保持应用程序运行)。

WCF 似乎足够聪明,可以意识到已经建立了到服务器的连接,并使用它。

有趣的是,当我重复上述过程,但在每次调用服务之间停止并重新启动应用程序时,我确实得到了与以下相同的行为HttpClient

在此处输入图片说明

还有一些其他潜在的问题ClientBase(例如见这里),我知道如果服务的流量相对较低或服务器设置为大量最大连接,临时打开的套接字可能根本不是问题,但我仍然希望能够可靠地测试这是否可能是一个问题,以及在什么条件下(例如,正在运行的 Windows 服务命中 WCF 服务与桌面应用程序)。

有什么想法吗?

usr*_*usr 3

WCF 不在HttpClient内部使用。WCF 可能使用HttpWebRequest该 API,因为当时该 API 可用,而且它可能更快一些,因为HttpClient它是它的包装器。

WCF 旨在用于高性能用例,因此他们确保重用 HTTP 连接。在我看来,默认情况下不重用连接是不可接受的。这要么是错误,要么是设计问题HttpClient

4.6.2 桌面 .NET Framework 在以下位置包含此行HttpClienthandler.Dispose

ServicePointManager.CloseConnectionGroups(this.connectionGroupName);
Run Code Online (Sandbox Code Playgroud)

由于此代码不在 CoreClr 中,因此没有相关文档。我不知道为什么要添加这个。this.connectionGroupName = RuntimeHelpers.GetHashCode(this).ToString(NumberFormatInfo.InvariantInfo);由于ctor的原因,它甚至还有一个错误。两个connectionGroupNames 可能会发生冲突。这是获取应该是唯一的随机数的糟糕方法。

如果重新启动该进程,则无法重用现有连接。这就是为什么你会在一个州看到旧的联系TIME_WAIT。这两个过程是不相关的。据它们(和操作系统)中的代码所知,它们不以任何方式进行合作。在进程重新启动时保存 TCP 连接也很困难(但可能)。据我所知,没有任何应用程序可以做到这一点。

您是否经常启动流程,以至于这可能会成为一个问题?不太可能,但如果是,您可以应用一种常规解决方法,例如减少TIME_WAIT持续时间。

复制这一点很容易:只需在循环中启动 100k 测试进程即可。