静态HttpClient仍在创建TIME_WAIT tcp端口

时间:2018-07-08 21:54:34

标签: c# .net-4.5

我正在使用.NET Framework的HttpClient(4.5.1 +,4.6.1和4.7.2)遇到一些有趣的行为。由于TCP端口使用率较高的已知问题,我在工作中的项目中提出了一些更改,以免每次使用时都丢弃HttpClient,请参见https://aspnetmonsters.com/2016/08/2016-08-27-httpclientwrong/

我研究了这些更改,以检查一切是否按预期进行,并发现我们仍然遇到与以前相同的TIME_WAIT端口。

为确认我建议的更改是正确的,我向应用程序添加了一些额外的跟踪,以确认我在整个应用程序中使用的是HttpClient的相同实例。此后,我使用了简单的测试应用程序(摘自上面链接的aspnetmonsters网站。

using System;
using System.Net.Http;

namespace ConsoleApplication
{
    public class Program
    {
        private static HttpClientHandler { UseDefaultCredentials = true };
        private static HttpClient Client = new HttpClient(handler);
        public static async Task Main(string[] args) 
        {
            Console.WriteLine("Starting connections");
            for(int i = 0; i<10; i++)
            {
                var result = await Client.GetAsync("http://localhost:51000");
                Console.WriteLine(result.StatusCode);
            }
            Console.WriteLine("Connections done");
            Console.ReadLine();
        }
    }
}

仅当使用Windows身份验证连接到IIS中托管的站点时,才会出现此问题。通过将身份验证设置为匿名(问题消失)并重新设置为Windows身份验证(问题再次出现),我可以轻松地重现该问题。

Windows身份验证的问题似乎并不限于提供程序的范围。如果您与我们进行谈判或与NTLM协商,则会遇到相同的问题。如果计算机只是工作站或域的一部分,也会发生此问题。

出于兴趣,我创建了一个dotnet core 2.1.0控制台应用程序,该问题根本不存在,并且可以正常工作。

TLDR:是否有人对如何解决此问题有任何想法,或者可能是一个错误?

1 个答案:

答案 0 :(得分:5)

简短版本

如果要通过NTLM身份验证重用连接,请使用.NET Core 2.1

长版

当使用NTLM身份验证时,“旧的” HttpClient确实为每个请求使用了不同的连接,我感到非常惊讶。这不是错误-在.NET Core 2.1 HttpClient使用HttpWebRequest之前,它将在每个NTLM身份验证调用之后关闭连接。

HttpWebRequest.UnsafeAuthenticatedConnectionSharing属性的文档中对此进行了描述,该属性可用于启用连接共享:

  

此属性的默认值为false,这将导致在请求完成后关闭当前连接。您的应用程序每次发出新请求时都必须经过身份验证序列。

     

如果将此属性设置为true,则执行身份验证后,用于检索响应的连接将保持打开状态。在这种情况下,其他将此属性设置为true的请求可以使用该连接,而无需重新进行身份验证。

风险在于:

  

如果已为用户A验证了连接,则用户B可以重用A的连接;用户B的请求是根据用户A的凭据完成的。

如果人们了解风险,并且应用程序使用模拟,则可以使用WebRequestHandler配置HttpClient并设置UnsafeAuthenticatedConnectionSharing,例如:

HttpClient _client;

public void InitTheClient()
{
    var handler=new WebRequestHandler
                { 
                    UseDefaultCredentials=true,
                    UnsafeAuthenticatedConnectionSharing =true
                };
    _client=new HttpClient(handler); 
}

WebRequestHandler 不会公开HttpWebRequest.ConnectionGroupName,该ID允许通过ID对连接进行分组,因此它无法处理模拟。

.NET Core 2.1

HttpClient在.NET Core 2.1中进行了重写,并实现了所有HTTP,使用套接字的网络功能,最小分配,连接池等。它还单独处理the NTLM challenge/response flow,因此同一套接字连接可用于提供不同的身份验证要求。

如果有兴趣的人,可以跟踪从HttpClient到SocketsHttpHanlder到HttpConnectionPoolManager,HttpConnectionPool,HttpConnection,AuthenticationHelper.NtAuth的调用,然后再返回到HttpConnection以发送原始字节。