服务突然抛出 SocketException 而没有明显的变化

Service suddenly throwing SocketException with no apparent changes

1 月 17 日 09:32,我们的一项服务突然开始抛出 500 个错误。它是第三方服务的适配器服务,我们使用 HttpClient 对其进行 POST (因此我们使用查询字符串参数对我们的服务进行 GET,然后使用POST 和正文中的参数)。当我使用 postman 或 curl 手动 post 到第三方服务时,它响应良好。所以这是我们服务的问题。它是使用 OWIN 中间件的 .NET 服务,类似于我认为的 .NET 核心工作方式。问题是前段时间,.NET 框架从 4.5.2 升级到 4.6,在 VS 中执行此操作时,它向 web.config 添加了一个 <httpRuntime targetFramework="4.5.2"/> 元素。这是为了尽最大努力保留应用程序的现有行为,以防框架版本之间发生任何重大更改。升级的人没有意识到,留在了web.config中的元素中。它工作了很长时间,然后突然在所有环境中同时(包括本地)被破坏。我认为它一定与 .NET 框架中的时间相关,但将我的系统时钟回滚并不能解决它!我可以寻找什么,关于这个谜团的任何想法?只需将 web.config 升级到 4.6 即可修复它,但我的任务是调查它。

这是潜在的错误:

System.Net.Sockets.SocketException (0x80004005): An existing connection was forcibly closed by the remote host
    at System.Net.Sockets.Socket.EndReceive(IAsyncResult asyncResult)
    at System.Net.Sockets.NetworkStream.EndRead(IAsyncResult asyncResult)

这是代码,它在 _client.PostAsync 处抛出上述内容作为 InnerException。 _client 是 System.Net.Http.HttpClient

public async Task<CalculateResponse> Calculate(CalculateRequest request)
{
    var env = new RequestEnvelope { Body = { RblsCalculate = request } };
    request.LoginId = _username;
    request.Password = _password;

    var body = XmlConvert.SerializeObject(env);

    var content = new StringContent(body, Encoding.UTF8, "application/soap+xml");
    var httpResponse = await _client.PostAsync(_endpointPath, content);

    var response = XmlConvert.ToObject<ResponseEnvelope>(await httpResponse.Content.ReadAsStreamAsync());

    return response?.Body?.RblsCalculateResponse;
}

第三方没有做任何改动,Windows更新没有运行(这同时影响了5个不同的环境)。我们没有做任何改变。当我们部署时,我们每次都部署到一个新实例,web.config 在服务器上没有改变,之前的部署是在几周前。

我查看了 4.6 的一些更改,如果不使用 TLSv1.0+ 作为协议,HttpClient 周围有一些潜在的重大更改,我已经在其中一台服务器上使用 Wireshark 检查过,我们正在使用 TLSv1.2。但这并不能解释为什么它突然停止了。

更新 - 根据@Trumpi 建议

SSL/TLS 跟踪的 trace.log 输出
System.Net.Sockets Verbose: 0 : [16292] Data from Socket#52088480::PostCompletion
System.Net.Sockets Verbose: 0 : [16292] 00000000 : 16 03 01 00 88 01 00 00-84 03 01 58 A4 49 35 01 : ...........X.I5.

更新 2 - 删除了不必要的日志 ^^

我的第一直觉是这是 TLS 握手的问题,第三方服务正在断开连接,因为它无法执行成功的握手。如您所述,TLS 版本可能是个问题。找不到兼容的密码可能是另一个问题。

我偶然发现 this blog post 描述了如何将握手信息写入跟踪文件。这是他添加到 web.config 文件的部分:

<system.diagnostics> <trace autoflush="true"/> <sources> <source name="System.Net" maxdatasize="1024"> <listeners> <add name="TraceFile"/> </listeners> </source> <source name="System.Net.Sockets" maxdatasize="1024"> <listeners> <add name="TraceFile"/> </listeners> </source> </sources> <sharedListeners> <add name="TraceFile" type="System.Diagnostics.TextWriterTraceListener" initializeData="trace.log"/> </sharedListeners> <switches> <add name="System.Net" value="Verbose" /> <add name="System.Net.Sockets" value="Verbose" /> </switches> </system.diagnostics>

这是我对问题中的信息所能做的最好的,我希望这能有所帮助。

编辑:发布结果后,调用似乎正在尝试协商服务器不再支持的 TLS 1.0 连接。我已将详细信息放在下面的评论中。

有趣的是,我上周遇到了一个非常相似的问题(尽管不是 .NET Core)。几个月来,我一直在通过日常工作调用 API 端点,突然间我遇到了同样的错误。我花了几天时间才找到修复方法,但对我来说,添加以下代码行解决了这个问题。您可以将它添加到方法的第一行。

ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12;