【问题标题】:Service suddenly throwing SocketException with no apparent changes服务突然抛出 SocketException 没有明显变化
【发布时间】:2017-02-15 11:41:59
【问题描述】:

1 月 17 日 09:32,我们的一项服务突然开始抛出 500 错误。它是第三方服务的适配器服务,我们使用 HttpClient 对其进行 POST(因此我们使用查询字符串参数对我们的服务进行 GET,然后使用 POST 和参数将其传输到第三方应用程序身体)。当我使用邮递员或 curl 手动发布到第三方服务时,它响应良好。所以这是我们服务的问题。它是使用 OWIN 中间件的 .NET 服务,类似于我认为的 .NET core 的工作方式。问题是前段时间,.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 的建议从 trace.log 输出 SSL/TLS 跟踪

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 - 删除了不必要的日志 ^^

【问题讨论】:

  • "当我使用 postman 或 curl 手动发布到第三方服务时,它响应良好。所以这是我们服务的问题 [...] 第三方没有制作任何更改” - 不同的是执行请求的时间。也许此时第三方服务超载,在升级过程中或其他任何情况下。如果运行 .NET 4.5.2 始终出现此错误而 .NET 4.6 没有,那么请检查两者之间 HTTP 标头的差异。
  • "被远程主机强制关闭"是从socket的错误一端调试问题。不知道为什么另一端决定放弃。跟着电线走。
  • @HansPassant 套接字的另一端是我无法访问的第 3 方应用程序。我确实联系了他们的支持团队,他们说他们看不到任何请求。

标签: c# .net owin dotnet-httpclient


【解决方案1】:

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

ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12;

【讨论】:

  • 非常好!即使我的 httpRuntime 仍然设置为 4.5.2,这确实可以解决问题。这真的很有帮助,谢谢。它没有解释为什么它突然停止了,但这是真正的进步。也许我们使用的是 Tls1.2 并且某些东西使它恢复到 1.0
  • 或者考虑一下,我更有可能认为我们一直在使用 1.0,而第 3 方进行了一些更改,从而放弃了支持。
  • 我也有同样的感觉。我花了很多时间试图弄清楚它为什么会发生,但过了一段时间我很高兴我找到了解决方案。如果您确实找出导致问题的原因,请告诉我。
  • @Rodders 我知道我在晚上与 Salesforce 合作,他们刚刚放弃了对 1.0 版的支持,不确定这是否是您的第三方供应商。
  • 除了@MDiesel,我们已经将解决​​方案实现为ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12 | SecurityProtocolType.Tls11;,以便在 1.2 不可用时使用 1.1。仅供参考。
【解决方案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 连接。我已将详细信息放在下面的评论中。

【讨论】:

  • 很好的发现,我会用输出更新我的帖子。谢谢
  • Data from Socket#52088480::PostCompletion之后的字节块中,第一个字节是内容类型(0x16),即握手。接下来的两个字节是版本(0x030x01),即 TLS 1.0。 TLS 1.2 发送0x030x03。来源RFC 5246 - TLS 1.2RFC 2246 - TLS 1.0
  • 我明白了。所以我们使用的是 TLS1.0。此外,如果我通过将 targetFramework 升级到 4.6 来解决此问题,请求可以工作,但我没有在该跟踪文件中获得任何日志。是否有可能我们使用的是 TLS1.2 并且某些东西使它恢复到 1.0?
  • 我会将@MDiesel 标记为答案,因为我认为这将对其他人最大的帮助并且最终是解决方法。但是非常感谢您提供这些信息,我今天学到了很多东西。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2013-05-27
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多