You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

.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时会继承当前登录用户的部分网络配置,存在偶发协商成功的概率,和问题描述的现象完全匹配。
  • 代码逻辑修正
    1. 放弃使用ServicePointManager.SecurityProtocol配置协议,.NET 6下直接在HttpClientHandler上显式指定支持的TLS版本:
      handler.SslProtocols = SslProtocols.Tls12 | SslProtocols.Tls13;
      
    2. 禁止每次请求新建HttpClient与HttpClientHandler实例,该写法会导致TCP端口耗尽、TLS会话复用失效,大幅提升连接失败概率。将HttpClient设为单例全局复用即可。
    3. 移除静态变量_certStatus的使用,证书回调中通过闭包捕获当前请求对应的CertStatus实例赋值,避免多请求并发时出现状态串扰,引发不可预期的逻辑错误。
  • 网络层拦截排查
    检查服务器本地防火墙、出口网关、SSL流量审计类软件的规则,这类设备/软件经常会篡改发往Azure的TLS握手包,导致远端强制断开连接。可在服务器上通过curl、浏览器连续访问目标Azure站点10次以上,若工具访问也存在偶发重置,优先排查网络层规则,无需优先调整代码。
  • 任务计划运行上下文对齐
    不要使用SYSTEM账号作为任务计划的运行账号,改为和手动运行程序时一致的普通用户账号,先勾选「仅在用户登录时运行」测试。若该配置下定时任务运行正常,说明此前使用的运行账号缺少系统TLS配置读取权限,或对应账号配置了特殊网络限制策略。

内容的提问来源于stack exchange,提问作者Three

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.29 04:39:21