【问题标题】:.NET: How to send TCP Keep Alive packets while downloading from HTTP API?.NET:从 HTTP API 下载时如何发送 TCP Keep Alive 数据包?
【发布时间】:2020-11-21 08:56:28
【问题描述】:

我正在尝试使用 .NET/C# 应用程序从 HTTP API 下载数据。即使 HttpClient 实例的超时设置为 30 分钟,请求超时也会快得多。今天我了解到这是因为 .NET HttpClient 不发送任何 TCP Keep Alive 数据包。 (这就是为什么我可以在 Chrome 中从该 API 下载数据的原因,正如 Wireshark 所证明的那样,Chrome 确实会发送这些数据包,而 HttpClient 不会。)

这就是我希望从 API 获取 JSON 数据的方式:

this.httpclient = new HttpClient();
[...]
result = await this.httpclient.GetAsync(url);

现在我做了一些研究,但我不知道如何发送这些 Keep Alive ping。 HttpClient 似乎不支持它们。所以我的猜测是打开连接时需要在套接字上启用这些。是否有任何解决方法,可能避免使用 HttpClient?我可以在套接字上以某种方式启用 Keep-Alive ping 吗?这是需要通过操作系统启用的功能吗?

请注意:不是关于 HTTP“Connection: Keep-Alive”标头的!您可以为 HttpClient 类启用此功能,但由于显而易见的原因,它不会触发任何 TCP Keep Alive 数据包。

编辑: 当客户端发送这些 Keep-Alive 数据包时,请求始终有效,否则它们将失败。这可以通过使用不同的客户端/浏览器来重现。最佳示例:Postman Chrome 扩展确实发送 Keep-Alive 数据包,因此请求有效。使用几乎相同的 Postman 独立客户端,请求失败(或不返回任何数据),因为有趣的是,独立客户端不发送任何可以使用 Wireshark 轻松检查的 Keep-Alive 数据包。

编辑:我为这个问题找到了一个非常简单的解决方案,请参阅下面的答案。

【问题讨论】:

    标签: c# .net tcp httpclient


    【解决方案1】:

    所以现在我最终回答了我自己的问题,因为我刚刚找到了一个真正有效的超级简单的解决方案:

    this.httpclient = new HttpClient();
    var sp = ServicePointManager.FindServicePoint(new Uri(url));
    sp.SetTcpKeepAlive(true, 30000, 30000);
    result = await this.httpclient.GetAsync(url);
    

    其实我用过

    var sp = ServicePointManager.FindServicePoint(new Uri(baseUrl));
    

    baseUrl 是我通过 for 循环发送的所有请求的公共文档根目录。

    【讨论】:

      【解决方案2】:

      HTTP(与 WebSockets 不同)不支持 keepalive 消息,所以我假设您指的是 SO_KEEPALIVE-style TCP keepalive 数据包。

      即使 HttpClient 实例的超时设置为 30 分钟,请求超时也会快得多。今天我了解到这是因为 .NET HttpClient 没有发送任何 TCP Keep Alive 数据包。

      我不认为这是正确的。发送保活数据包不会对超时行为产生任何影响。特别是,当 keepalive 到达服务器并发送 ack 响应时,甚至都没有通知应用程序 - 实际上它们无法得到通知,因为 TCP keepalive 或 ack 中没有数据。

      现在我做了一些研究,但我不知道如何发送这些 Keep Alive ping。

      TCP keepalive 数据包存在问题的原因有两个:它们具有尴尬的默认值,并且它们可以被中间路由器丢弃。尴尬的默认设置包括 最少 2 小时的计时器,并且不保证能够更改它;幸运的是,现代 Windows 版本确实允许设置每个连接的保活计时器,并允许将其设置为低得多的值。

      也就是说,我不知道有一种方法可以访问底层的Socket 以获得HttpClientHttpClient 有一个处理程序管道,这些处理程序以 SocketsHttpHandler 结束(这些天),它实际上负责套接字连接。但是我没有看到任何那种类型的 API 可以让你直接进入来操作套接字,或者在构造过程中提供一个已经配置好的套接字。

      【讨论】:

      • 感谢您强调这一点,我原以为有一种方法可以以某种方式调整套接字设置。关于对超时行为的影响,我可以清楚地看到这些保活数据包的使用对请求是否有效有影响。 (我已经用截图更新了我的问题)
      【解决方案3】:

      这就是我假设正在发生的事情:

      您正在调用await this.httpclient.GetAsync(url);,而url 的端点正在执行大量处理...不仅仅是在整个过程中下载文件或数据,而且只是处理数据,在完成之前不会返回任何内容。

      我猜处理时间超过 2 分钟,然后在那个标记附近消失,对吗?如果您没有将服务器配置为允许响应时间超过 2 分钟,那么服务器将断开您的连接……这是有充分理由的。

      套接字非常昂贵。您的端点应尽快返回数据,因为如果不这样做,您可能会遇到套接字饥饿问题。

      例如,ASP.NET Core 应用程序的默认超时为 2 分钟。您可以在此处的默认 Web.Config 中找到它:

      所有这些关于“TCP keep alive”的东西都与你所经历的无关。从来没有。

      【讨论】:

      • 是的,端点正在执行大量处理。不幸的是,我对服务器没有任何控制权,我无法在那里更改任何内容,我怀疑他是否会听到我增加超时的要求(尽管会试一试)。但是,我几乎无法相信这与我所经历的事情无关,因为我可以清楚地看到当客户端发送 TCP 保持活动数据包时我会取回数据,并且当它们不存在时根本没有响应.这可以通过各种客户端进行复制。
      • 好吧——但你知道,问题不在于HttpClient,而在于其他东西。无论是您的计算机、网络还是服务器。我的钱还在服务器上。祝你好运!
      猜你喜欢
      • 1970-01-01
      • 2016-09-07
      • 1970-01-01
      • 1970-01-01
      • 2013-03-25
      • 2016-03-20
      • 1970-01-01
      • 2011-02-13
      • 1970-01-01
      相关资源
      最近更新 更多