.Net 核心 HttpClient 错误? SocketException:一个现有的连接被远程主机强行关闭

Posted

技术标签:

【中文标题】.Net 核心 HttpClient 错误? SocketException:一个现有的连接被远程主机强行关闭【英文标题】:.Net core HttpClient bug? SocketException: An existing connection was forcibly closed by the remote host 【发布时间】:2019-01-14 20:51:22 【问题描述】:

以下代码在完整的 .Net 框架控制台程序中运行没有任何错误。但是,在.Net core 2.1中运行时出现以下错误。

System.AggregateException H结果=0x80131500 Message=发生了一个或多个错误。 源=System.Private.CoreLib 堆栈跟踪: 在 System.Threading.Tasks.Task`1.GetResultCore(Boolean waitCompletionNotification) 在 C:\source\repos\ConsoleApp1\Program.cs:line 38 中的 ConsoleApp1.Program.requestString(String url) 在 C:\source\repos\ConsoleApp1\Program.cs:line 13 中的 ConsoleApp1.Program.Main(String[] args) 内部异常1: HttpRequestException:无法建立 SSL 连接,请参阅内部异常。 内部异常 2: IOException:无法从传输连接中读取数据:现有连接被远程主机强行关闭。 内部异常 3: SocketException:一个现有的连接被远程主机强行关闭

代码:

class Program

    static void Main(string[] args)
    
        var url = "https://google.com";
        var (statusCode, html) = requestString(url);
        Console.WriteLine("%d %s", statusCode, html);
    

    static CookieContainer cc = new CookieContainer();

    static HttpClientHandler handler = new HttpClientHandler  AllowAutoRedirect = false, CookieContainer = cc ;

    public static async Task<(int statusCode, string content)> requestStringAsync(string url)
    
        ServicePointManager.ServerCertificateValidationCallback += (sender, cert, chain, sslPolicyErrors) => true;
        ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls | SecurityProtocolType.Tls11 | SecurityProtocolType.Tls12;
                                                                     // | SecurityProtocolType.Ssl3;
        using (var request = new HttpRequestMessage  RequestUri = new Uri(url), Method = HttpMethod.Get )
        using (var client = new HttpClient(handler))
        
            var response = await client.SendAsync(request); // Error (actual line)
            // response.EnsureSuccessStatusCode() |> ignore
            var statusCode = (int)response.StatusCode;
            var content = await response.Content.ReadAsStringAsync();
            return (statusCode, content);
        
    

    public static (int statusCode, string content) requestString(string url)
    
        return requestStringAsync(url).Result;
    

【问题讨论】:

你试过dotnet dev-certs https --trust吗? 【参考方案1】:

.NET Core 2.1 Preview 的 bug 提到了这个问题。这可能是原因。但是,我也注意到您的 TLS 设置不正确。您当前正在启用它,但会覆盖已设置的所有其他协议。而不是这个:

ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls | SecurityProtocolType.Tls11 | SecurityProtocolType.Tls12;

你应该使用这个:

ServicePointManager.SecurityProtocol |= SecurityProtocolType.Tls | SecurityProtocolType.Tls11 | SecurityProtocolType.Tls12;
// ----------------------------------^

我认为这是一个次要问题,但同样值得解决。

更新

上面引用的 GitHub 问题有一个讨论,最终链接到.NET Core 2.1 SDK Preview 2 的官方公告。它有以下说法:

Sockets 性能和 SocketsHttpHandler

我们对 .NET Core 2.1 中的套接字进行了重大改进。套接字是传出和传入网络通信的基础。 .NET Core 2.1 中更高级别的网络 API,包括 HttpClient 和 Kestrel,现在基于 .NET 套接字。在早期版本中,这些更高级别的 API 基于本机网络实现。

...

您可以使用以下机制之一来配置进程以使用旧的 HttpClientHandler:

从代码中,使用 AppContext 类:

AppContext.SetSwitch("System.Net.Http.UseSocketsHttpHandler", false);

AppContext 开关也可以通过配置文件来设置。

同样可以通过环境变量 DOTNET_SYSTEM_NET_HTTP_USESOCKETSHTTPHANDLER 来实现。要选择退出,请将值设置为 false 或 0。

【讨论】:

我已更新 SecurityProtocal 分配,但仍然出现异常。 @ca9163d9 我已经为你更新了我的答案。我认为值得阅读链接的 GitHub 问题以及 MSDN 公告。【参考方案2】:

在我的情况下,根本原因是使用 HttpClient.GetAsync(url) 的多个并发调用。我通过将 HttpClient 实例设置为单例来修复它。

更多信息here。

因此,HttpClient 旨在被实例化一次并在应用程序的整个生命周期中重复使用。为每个请求实例化一个 HttpClient 类将耗尽重负载下可用的套接字数量。该问题将导致 SocketException 错误。解决该问题的可能方法是基于将 HttpClient 对象创建为单例或静态...

【讨论】:

以上是关于.Net 核心 HttpClient 错误? SocketException:一个现有的连接被远程主机强行关闭的主要内容,如果未能解决你的问题,请参考以下文章

如何在 .net 和 .net 核心中使用 HttpClient 调用多个客户端 API

System.Net.Http.HttpClient 在 .net 核心和 .net 框架中的行为不同

.net core HttpClient 使用之消息管道解析

.net core HttpClient 使用之消息管道解析

ASP.NET HttpClient 请求返回 409 错误

《ASP.NET Core 6框架揭秘》实例演示[18]:HttpClient处理管道