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秒后取消每个请求,你也可以将_httpClient
的Timeout
属性设置为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);
}
}
}
我正在尝试将 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秒后取消每个请求,你也可以将_httpClient
的Timeout
属性设置为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);
}
}
}