.NET6 HttpClient访问Azure站点报连接被远程主机强制关闭异常
问题描述
基于.NET 6开发的控制台应用用于遍历网站列表,检测SSL证书故障与即将到期的证书。除托管在Azure上的站点外,其余站点检测逻辑均运行正常。本地调试环境访问Azure站点无异常,但部署到Windows Server 2012服务器运行时,10次HTTP GET请求中有9次抛出以下异常:
System.Net.Http.HttpRequestException: The SSL connection could not be established, see inner exception. ---> System.IO.IOException: Unable to read data from the transport connection: An existing connection was forcibly closed by the remote host.. ---> System.Net.Sockets.SocketException (10054):
问题存在特殊表现:请求偶发成功、偶发失败;通过Windows任务计划程序每日定时运行时请求全部失败,手动运行exe时偶尔可请求成功。
核心实现代码
async Task<CertStatus> SendRequest(string url) { _certStatus = new CertStatus { URL = url, Success = false }; var handler = new HttpClientHandler(); handler.ServerCertificateCustomValidationCallback = ServerCertificateCustomValidation; var client = new HttpClient(handler); try { var response = await client.GetAsync(url); response.EnsureSuccessStatusCode(); _certStatus.Success = true; } catch (HttpRequestException ex) { _certStatus.ErrorMessage = ex.Message; } handler.Dispose(); client.Dispose(); var retval = _certStatus; return retval; } static bool ServerCertificateCustomValidation(HttpRequestMessage requestMessage, X509Certificate2? certificate, X509Chain? chain, SslPolicyErrors sslErrors) { if(_certStatus != null && certificate != null) { _certStatus.ExpireDate = DateTime.Parse(certificate.GetExpirationDateString()); } Console.WriteLine($"Requested URI: {requestMessage.RequestUri}"); Console.WriteLine($"Effective date: {certificate?.GetEffectiveDateString()}"); Console.WriteLine($"Exp date: {certificate?.GetExpirationDateString()}"); Console.WriteLine($"Issuer: {certificate?.Issuer}"); Console.WriteLine($"Subject: {certificate?.Subject}"); Console.WriteLine($"Errors: {sslErrors}"); return sslErrors == SslPolicyErrors.None; }
已尝试的修复方案(均未解决问题)
- 配置请求头主动关闭TCP连接:
client.DefaultRequestHeaders.ConnectionClose = true;
- 将请求超时时间增大至120秒:
client.Timeout = TimeSpan.FromSeconds(120);
- 显式设置SSL协议为TLS 1.2:
System.Net.ServicePointManager.SecurityProtocol |= System.Net.SecurityProtocolType.Tls12;
排查与解决方案
- 系统TLS配置修复(优先级最高)
Windows Server 2012默认未完整支持TLS 1.2,且缺少Azure边缘节点要求的现代TLS加密套件,这是该问题的核心诱因。先安装系统更新KB3140245,为Win2012补全TLS 1.2客户端支持;安装完成后通过注册表配置,强制系统默认启用TLS 1.2、TLS 1.3,禁用SSL 3.0、TLS 1.0等老旧协议。
任务计划程序运行时默认读取系统级网络配置,系统TLS配置错误时,定时任务发起的TLS握手会直接被Azure节点拒绝,表现为100%请求失败;手动运行exe时会继承当前登录用户的部分网络配置,存在偶发协商成功的概率,和问题描述的现象完全匹配。 - 代码逻辑修正
- 放弃使用
ServicePointManager.SecurityProtocol配置协议,.NET 6下直接在HttpClientHandler上显式指定支持的TLS版本:handler.SslProtocols = SslProtocols.Tls12 | SslProtocols.Tls13; - 禁止每次请求新建
HttpClient与HttpClientHandler实例,该写法会导致TCP端口耗尽、TLS会话复用失效,大幅提升连接失败概率。将HttpClient设为单例全局复用即可。 - 移除静态变量
_certStatus的使用,证书回调中通过闭包捕获当前请求对应的CertStatus实例赋值,避免多请求并发时出现状态串扰,引发不可预期的逻辑错误。
- 放弃使用
- 网络层拦截排查
检查服务器本地防火墙、出口网关、SSL流量审计类软件的规则,这类设备/软件经常会篡改发往Azure的TLS握手包,导致远端强制断开连接。可在服务器上通过curl、浏览器连续访问目标Azure站点10次以上,若工具访问也存在偶发重置,优先排查网络层规则,无需优先调整代码。 - 任务计划运行上下文对齐
不要使用SYSTEM账号作为任务计划的运行账号,改为和手动运行程序时一致的普通用户账号,先勾选「仅在用户登录时运行」测试。若该配置下定时任务运行正常,说明此前使用的运行账号缺少系统TLS配置读取权限,或对应账号配置了特殊网络限制策略。
内容的提问来源于stack exchange,提问作者Three
相关产品推荐
相关产品推荐

