HttpClient 取消不会终止底层 TCP 调用

HttpClient cancellation doesn't kill underlying TCP call

我正在尝试将 HttpClient 调用的默认超时设置为 5 秒。

我已经通过 CancellationTokenSource 完成了此操作。

这是相关的代码:

var cancellationToken = new CancellationTokenSource();
cancellationToken.CancelAfter(TimeSpan.FromSeconds(5));
var result = _httpClient.SendAsync(request, cancellationToken.Token);

在调用代码出现 "Task was cancelled" 错误方面按我的预期工作(我在 .NET 4.7 控制台应用程序中测试),但我注意到在 Fiddler 中请求仍然是 运行 1分钟,直到终于放弃:

有人可以解释这种行为吗?

我希望基础请求在取消被触发时也被取消。

_httpClient 被实例化为:new HttpClient { BaseAddress = baseAddress }

我知道有 Timeout 设置,但不确定我是否应该使用该设置或取消标记?我的猜测是 Timeout 是针对 non-async/await 个案例?

正如 Damien 在评论中所说,HttpClient 尽可能重复使用连接,这就是取消时未关闭连接的原因。

当取消这样的请求时,HttpClient 只会停止 sending/receiving 数据 to/from 另一端。它不会发送任何信息来通知另一端它已被取消。因此,您看到的 1 分钟超时取决于连接另一端的行为。

另外,如果你想在5秒后取消每个请求,你也可以将_httpClientTimeout 属性设置为TimeSpan.FromSeconds(5)。行为将完全相同(如果另一端在 5 秒内未响应,将抛出 TaskCanceledException)。

如果有人感兴趣,您可以尝试使用以下方法为每个 HttpClient 请求应用您自己的超时。它似乎对我有用,将 SendAsync() 限制为 2 秒并在发生超时时立即返回:

private async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, TimeSpan? timeout = null)
{
    if (timeout is null)
    {
        return await _httpClient.SendAsync(request);
    }
    else
    {
        using (var cts = new CancellationTokenSource(timeout.Value))
        {
            var sendTask = _httpClient.SendAsync(request);

            while (!sendTask.IsCompleted)
            {
                cts.Token.ThrowIfCancellationRequested();
                await Task.Delay(10).ConfigureAwait(false);
            }

            return await sendTask.ConfigureAwait(false);
        }
    }
}