.net NET:如何在从HTTP API下载时发送TCP保持活动数据包?

fjaof16o  于 2023-02-26  发布在  .NET
关注(0)|答案(3)|浏览(134)

使用.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);

现在我做了一些研究,但我找不到如何发送这些保活ping。HttpClient似乎不支持它们。所以我的猜测是,当打开连接时,这些需要在套接字上启用。有什么解决办法吗,也许是避免HttpClient?我可以在套接字上以某种方式启用保活ping吗?这是一个需要通过操作系统启用的功能吗?

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

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

**编辑:**我已经找到了这个问题的一个非常简单的解决方案,请看下面我的答案。

lo8azlld

lo8azlld1#

所以现在我结束回答我自己的问题,因为我刚刚找到了一个超级简单的解决方案,实际上工作:

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循环发送的所有请求的公共文档根。

jm2pwxwz

jm2pwxwz2#

HTTP(与WebSockets不同)不支持keepalive消息,因此我假设您指的是SO_KEEPALIVE样式的TCP keepalive包。
即使HttpClient示例的超时设置为30分钟,请求超时的速度也会快得多。今天我了解到这是因为.NET HttpClient不发送任何TCP Keep Alive数据包。
我不认为这是正确的。发送keepalive数据包不会对超时行为产生任何影响。特别是,当keepalive到达服务器并发送ack响应时,两个应用程序甚至都没有得到通知-事实上,它们 * 不能 * 得到通知,因为TCP keepalive或ack中没有数据。
现在我做了一些研究,但我找不出如何发送这些保持活动的ping。
TCP keepalive数据包存在问题有两个原因:它们有很难设置的默认值,而且可能会被中间路由器丢弃。2这些默认值包括最少2小时的计时器,而且不能保证有能力改变它;幸运的是,现代Windows版本确实允许设置每个连接的保活计时器,并且允许将其设置为低得多的值。
也就是说,我不知道有什么方法可以到达HttpClient的底层Socket。(现在)在SocketsHttpHandler中,它实际上负责套接字连接。但是我没有看到任何API可以让你直接进入来操作套接字,或者在构造期间提供已经构造好的插座。

1l5u6lss

1l5u6lss3#

这里是NetCore 2. 1以后的另一个解决方案。我发现ServicePointManager.FindServicePoint(new Uri(url));代码在我的情况下不起作用,这个起作用了。
来源www.example.comhttps://learn.microsoft.com/en-us/dotnet/api/system.net.http.socketshttphandler.connectcallback?view=net-7.0#examples

using SocketsHttpHandler handler = new SocketsHttpHandler();

    handler.ConnectCallback = async (ctx, ct) =>
    {
        var s = new Socket(SocketType.Stream, ProtocolType.Tcp) { NoDelay = true };
        try
        {
            s.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.KeepAlive, true);
            s.SetSocketOption(SocketOptionLevel.Tcp, SocketOptionName.TcpKeepAliveTime, 5);
            s.SetSocketOption(SocketOptionLevel.Tcp, SocketOptionName.TcpKeepAliveInterval, 5);
            s.SetSocketOption(SocketOptionLevel.Tcp, SocketOptionName.TcpKeepAliveRetryCount, 5); //note this doesnt work on some windows versions

            await s.ConnectAsync(ctx.DnsEndPoint, ct);
            return new NetworkStream(s, ownsSocket: true); 
        }
        catch
        {
            s.Dispose();
            throw;
        }
    };

    // Create an HttpClient object
    using HttpClient client = new HttpClient(handler);

相关问题