C# 中的 HttpClient 和超时的完整异常处理

问题描述 投票:0回答:1

我正在尝试构建一个 wpf 应用程序,它将充当某些设备的子网扫描仪。一些 IP 地址将回复响应,而其他 IP 地址将由于没有接收者请求而超时。我正在使用 Task.WhenAll() 异步执行所有请求,并且当达到 HttpClient.Timeout 时,任务将按预期取消,并且 UI 仍然响应。过了一会儿,UI 冻结,并且 HttpRequestExceptions 和 SocketExceptions 出现在已取消的线程中。我也尝试使用取消令牌,但行为相同。 我可以取消来自 HttpClient 的完整请求,还是可以更好地处理异常以使 UI 不冻结?

这是一段代码,其中包含一个可以正常工作的 URL 和一个不会回复且超时的 URL 示例。

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Net.Http;
using System.Threading.Tasks;
using System.Windows;

namespace TestWPF
{
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();
        }

        public async Task<string> GetStatus(string url)
        {
            using HttpClient client = new HttpClient();
            client.Timeout = TimeSpan.FromSeconds(2);

            try
            {
                var HTTPResponse = await client.GetStreamAsync(url);
                string response = new System.IO.StreamReader(HTTPResponse).ReadToEnd();
                return response;
            }
            catch (Exception e)
            {
                throw;
            }
        }

        private async void Button_Click(object sender, RoutedEventArgs e)
        {
            List<Task> tasks = new List<Task>
            {
                GetStatus("https://api.thecatapi.com/v1/images/search"),
                GetStatus("https://192.168.0.31")
            };

            try
            {
                await Task.WhenAll(tasks);
            }
            catch (Exception ex)
            {
                Debug.WriteLine($"Exception: {ex}");
            }

            // Loop results
            var successfulTasks = tasks.Where(task => task.Status == TaskStatus.RanToCompletion);

            // Process the results of successful tasks
            foreach (var successfulTask in successfulTasks)
            {
                // Get the return value inside the task
                string scanResult = ((Task<string>)successfulTask).Result;
                Debug.WriteLine(scanResult);
            }
        }
    }
}

以及由此而来的例外。可以看出,HttpRequestException 和 SocketException 是在任务取消后出现的。

Exception thrown: 'System.Threading.Tasks.TaskCanceledException' in System.Private.CoreLib.dll
Exception thrown: 'System.Threading.Tasks.TaskCanceledException' in System.Private.CoreLib.dll
Exception thrown: 'System.Threading.Tasks.TaskCanceledException' in System.Private.CoreLib.dll
Exception thrown: 'System.Threading.Tasks.TaskCanceledException' in System.Private.CoreLib.dll
Exception thrown: 'System.Threading.Tasks.TaskCanceledException' in System.Private.CoreLib.dll
Exception thrown: 'System.Threading.Tasks.TaskCanceledException' in System.Net.Http.dll
Exception thrown: 'System.Threading.Tasks.TaskCanceledException' in System.Private.CoreLib.dll
Exception thrown: 'System.Threading.Tasks.TaskCanceledException' in TestWPF.dll
Exception thrown: 'System.Threading.Tasks.TaskCanceledException' in System.Private.CoreLib.dll
Exception: System.Threading.Tasks.TaskCanceledException: The request was canceled due to the configured HttpClient.Timeout of 2 seconds elapsing.
 ---> System.TimeoutException: A task was canceled.
 ---> System.Threading.Tasks.TaskCanceledException: A task was canceled.
   at System.Threading.Tasks.TaskCompletionSourceWithCancellation`1.WaitWithCancellationAsync(CancellationToken cancellationToken)
   at System.Net.Http.HttpConnectionPool.GetHttp11ConnectionAsync(HttpRequestMessage request, Boolean async, CancellationToken cancellationToken)
   at System.Net.Http.HttpConnectionPool.SendWithVersionDetectionAndRetryAsync(HttpRequestMessage request, Boolean async, Boolean doRequestAuth, CancellationToken cancellationToken)
   at System.Net.Http.RedirectHandler.SendAsync(HttpRequestMessage request, Boolean async, CancellationToken cancellationToken)
   at System.Net.Http.HttpClient.GetStreamAsyncCore(HttpRequestMessage request, CancellationToken cancellationToken)
   --- End of inner exception stack trace ---
   --- End of inner exception stack trace ---
   at System.Net.Http.HttpClient.HandleFailure(Exception e, Boolean telemetryStarted, HttpResponseMessage response, CancellationTokenSource cts, CancellationToken cancellationToken, CancellationTokenSource pendingRequestsCts)
   at System.Net.Http.HttpClient.GetStreamAsyncCore(HttpRequestMessage request, CancellationToken cancellationToken)
   at TestWPF.MainWindow.GetStatus(String url)
   at TestWPF.MainWindow.Button_Click(Object sender, RoutedEventArgs e)
[{"id":"9l6","url":"https://cdn2.thecatapi.com/images/9l6.jpg","width":720,"height":960}]
The thread 0x3a0c has exited with code 0 (0x0).
Exception thrown: 'System.Net.Sockets.SocketException' in System.Net.Sockets.dll
Exception thrown: 'System.Net.Sockets.SocketException' in System.Private.CoreLib.dll
Exception thrown: 'System.Net.Http.HttpRequestException' in System.Net.Http.dll
Exception thrown: 'System.Net.Http.HttpRequestException' in System.Private.CoreLib.dll
Exception thrown: 'System.Net.Http.HttpRequestException' in System.Private.CoreLib.dll
Exception thrown: 'System.Net.Http.HttpRequestException' in System.Private.CoreLib.dll
c# .net exception timeout httpclient
1个回答
0
投票

您在

ReadToEnd
陷入僵局。您需要使用
await ReadToEndAsync
。您还应该处理掉这些物品。

此外,如果出现 DNS 故障,

client.Timeout
有时不会超时,直到 15 秒结束。所以请使用
CancellationToken
来代替。

您还应该使用单个

HttpClient
,以避免套接字耗尽。

static HttpClient _client = new HttpClient() { Timeout = TimeSpan.FromSeconds(2) };

public async Task<string> GetStatus(string url)
{
    using var cts = new cancellationTokeSource(2000);
    using var HTTPResponse = await _client.GetStreamAsync(url, cts.Token);
    using var sr = new StreamReader(HTTPResponse);
    string response = await sr.ReadToEndAsync(cts.Token);
    return response;
}
© www.soinside.com 2019 - 2024. All rights reserved.