C# 静态HttpClient仍在创建TIME\u WAIT tcp端口

C# 静态HttpClient仍在创建TIME\u WAIT tcp端口,c#,.net-4.5,C#,.net 4.5,我在.NET Framework(4.5.1+、4.6.1和4.7.2)中遇到了一些与HttpClient有关的有趣行为。我在一个正在工作的项目中提出了一些更改,以避免在每次使用时都处理HttpClient,因为TCP端口使用率高的已知问题,请参阅 我已经调查了这些变化,以检查事情是否按预期工作,并发现我们仍在经历与以前相同的时间等待端口 为了确认我提出的更改是正确的,我向应用程序添加了一些额外的跟踪,以确认我在整个应用程序中使用的是同一个HttpClient实例。此后,我使用了简单的测试应用程

我在.NET Framework(4.5.1+、4.6.1和4.7.2)中遇到了一些与HttpClient有关的有趣行为。我在一个正在工作的项目中提出了一些更改,以避免在每次使用时都处理HttpClient,因为TCP端口使用率高的已知问题,请参阅

我已经调查了这些变化,以检查事情是否按预期工作,并发现我们仍在经历与以前相同的时间等待端口

为了确认我提出的更改是正确的,我向应用程序添加了一些额外的跟踪,以确认我在整个应用程序中使用的是同一个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();
        }
    }
}
使用系统;
使用System.Net.Http;
命名空间控制台应用程序
{
公共课程
{
私有静态HttpClientHandler{UseDefaultCredentials=true};
私有静态HttpClient客户端=新HttpClient(处理程序);
公共静态异步任务主(字符串[]args)
{
控制台写入线(“启动连接”);

对于(int i=0;i短版本

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

长版本

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

这在可用于共享连接的属性文档中有描述:

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

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

风险在于:

如果已为用户a验证连接,则用户B可以重用a的连接;用户B的请求基于用户a的凭据得到满足

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

WebRequestHandler不公开允许按ID分组连接的,因此无法处理模拟

.NET核心2.1

HttpClient是在.NET Core 2.1中重写的,它实现了所有HTTP、使用套接字的联网功能、最小分配、连接池等。它还单独处理,因此相同的套接字连接可以用于服务不同的认证请求


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

没有“TCP端口使用率高的已知问题”.那篇文章说,你不需要处置HttpClient,因为它是可重用的和线程安全的。它还说,你不应该处置它,因为它可以像WebClient和原始HttpWebRequest那样重用SSL通道、套接字等。你使用静态HttpClient是因为你可以,而不是因为任何问题“TCP端口使用率高的已知问题"“。那篇文章似乎有不同的观点。这不是一个bug,因为它是预期的行为,建议不要每次请求都处理HttpClient。即使是Microsoft模式和实践也建议使用相同的配置。通过简单地对具有Windows身份验证的IIS运行示例应用程序,您可以看到他提出了一个问题。使用Sysinternal中的TcpView,您可以看到每个请求还剩下1个端口等待时间。不,没有。它没有说HttpClient有问题,甚至没有暗示这一点。它说您可以重用HttpClient实例并保存一些套接字。这也不是什么新鲜事,这是自2012年以来就知道的。尽管这篇文章可能是现在最流行的是无需复制。运行文章的代码的行为符合预期。使用TcpView表明使用静态HttpClient不会留下任何套接字。每次使用新HttpClient运行它都会留下10个套接字打开。在使用静态客户端运行测试之前,我必须等待这些套接字关闭。此外,使用静态c客户端带来了更好的性能,因为它不必每次都执行DNS查找和建立SSL通道。忘记套接字。这是100倍的改进。这应该足以说服任何人使用共享HttpClient实例,特别是如果你进行大量HTTP调用,我可以看出最好的方法是se将迁移到.NETCore。我怀疑这是出于设计,而不是一个bug,但很高兴知道。有深刻的解释
HttpClient _client;

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