1

我有一个包含 350 个可下载图片网址的列表。我通过运行多个任务一次并行下载 10 张图像。但是在突然下载了N张图片后,我的代码抛出了以下异常。

异常:“发送请求时发生错误。”

InnerException:“请求被中止:无法创建 SSL/TLS 安全通道。”

StackTrace:“在 System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(任务任务)\r\n 在 System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(任务任务)\r\n 在 System.Runtime.CompilerServices.TaskAwaiter`1。获取结果()\r\n ...

我创建了一个示例项目来重现此异常。我手上有2个测试用例。您可以从My Sky Drive Here下载正在运行的测试项目。右键单击文件 HTTPClientTestCases1and2.zip 并下载。

案例 1:使用单个实例 HttpClient 进行所有图像下载。

在这种情况下,我使用相同的 HttpClient 向 10 个 url 发送并行请求。在这种情况下,大部分时间下载是成功的。在最后一次成功下载图像后,等待最少 40 秒(最多 1 分 40 秒)以发送下一批的下一个并行下载请求。由于此异常,一个图像肯定会失败。但是有很多地方写并建议使用单个 HttpClient 进行多个请求。

   public async void DownloadUsingSingleSharedHttpClient(Int32 imageIndex)
    {   
        Uri url = new Uri(ImageURLs[imageIndex]);

        UnderDownloadCount++;

        try
        {
            Byte[] contentBytes = null;

            try
            {
                // Exception IS THROWN AT LINE BELOW 
                HttpResponseMessage response = await _httpClient.GetAsync(url);

                contentBytes = await response.Content.ReadAsByteArrayAsync();
            }
            catch (Exception ex)
            {
                System.Diagnostics.Debug.WriteLine("Download Failed at GetAsync() :" + ex.Message);
                throw ex;
            }

            DownloadedCount++;

            if (OnSuccess != null)
                OnSuccess(this, new DownloadSuccessEventArgs() { Index = imageIndex, Data = contentBytes });
        }
        catch (HttpRequestException hre)
        {
            DownloadFailedCount++;
            if (OnFailed != null)
                OnFailed(hre, null);
        }
        catch (TaskCanceledException hre)
        {   
            DownloadFailedCount++;
            if (OnFailed != null)
                OnFailed(hre, null);
        }
        catch (Exception e)
        {
            DownloadFailedCount++;
            if (OnFailed != null)
                OnFailed(e, null);
        }
    }

案例 2:为每个图像创建新的 HttpClient 实例下载

在这种情况下,它只是在并行下载图像时由于相同的异常而非常频繁地失败。

public async void DownloadUsingCreatingHttpClientEveryTime(Int32 imageIndex)
{
    Uri url = new Uri(ImageURLs[imageIndex]);

    UnderDownloadCount++;
    try
    {
        Byte[] contentBytes = null;

        using (HttpClientHandler _handler = new HttpClientHandler())
        {
            _handler.AllowAutoRedirect = true;
            _handler.MaxAutomaticRedirections = 4;

            using (HttpClient httpClient = new HttpClient(_handler))
            {
                httpClient.DefaultRequestHeaders.ExpectContinue = false;
                httpClient.DefaultRequestHeaders.Add("Keep-Alive", "false");

                try
                {
                    // Exception IS THROWN AT LINE BELOW 
                    contentBytes = await httpClient.GetByteArrayAsync(url.OriginalString);
                }
                catch (Exception ex)
                {
                    System.Diagnostics.Debug.WriteLine("Download Failed :" + ex.Message);
                    throw ex;
                    }
                }

            _handler.Dispose();
        }

        DownloadedCount++;

        if (OnSuccess != null)
            OnSuccess(this, new DownloadSuccessEventArgs() { Index = imageIndex, Data = contentBytes });
    }
    catch (HttpRequestException hre)
    {
        DownloadFailedCount++;
        if (OnFailed != null)
            OnFailed(hre, null);
    }
    catch (TaskCanceledException hre)
    {
        DownloadFailedCount++;
        if (OnFailed != null)
            OnFailed(hre, null);
    }
    catch (Exception e)
    {
        DownloadFailedCount++;
        if (OnFailed != null)
            OnFailed(e, null);
    }
}

请编辑 MainPage.xaml.cs 中的以下函数以检查两种情况

 private void Send10DownloadRequestParallel()
    {
        for (Int32 index = 0; index < 10; index++)
        {
            Task.Run(() =>
            {   
                Int32 index1 = rand.Next(0, myImageDownloader.ImageURLs.Count - 1);

                UpdateDownloadProgress();

                // Case 1: Download Using Single Shared HttpClient
                // myImageDownloader.DownloadUsingSingleSharedHttpClient(index1);

                // OR

                // Case 2: Download Using Creating Http Client Every Time
                myImageDownloader.DownloadUsingCreatingHttpClientEveryTime(index1);
            });
        }
    }

我的问题:我做错了什么?通过克服此异常,在 WinRT 中实现并行下载器的最佳方法是什么。

4

1 回答 1

2

我运行了您的示例应用程序,但仅在以下几种情况下出现错误:

  1. 当您的应用请求的图像不存在时,.NET HTTP 客户端会引发异常。您的处理程序不能完全处理这种情况,因为内部异常是 NULL。我不得不稍微调整一下代码:

    async void myImageDownloader_OnFailed(object sender, EventArgs e)
    {
        await App.CurrentDispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.Normal, delegate
        {   
            TimeSpan time =(DateTime.Now -dateTimeSuccess);
    
            String timeGap = "Ideal For:" + time.ToString() + "\n";
            ErrorListBox.Text += "\n Failed When: " + DownloadInfo.Text + "\n";
            ErrorListBox.Text += timeGap;
    
            // CX - added null check for InnerException, as these are NULL on HTTP result status 404
            var ex = sender as Exception;
            if (ex.InnerException != null)
                ErrorListBox.Text += ex.InnerException.Message;
            else
                ErrorListBox.Text += "Inner Exception null - Outer = (" + ex.ToString() + ")";
        });
    }
    
  2. 我唯一一次得到你的另一个错误Could not create SSL/TLS secure channel in Windows 8 Metro App,是当我使用 HTTP 调试代理(Fiddler)时。如果我不使用拦截所有 HTTP(S) 调用的 Fiddler,那么我下载没有问题。我什至开始快速连续下载多个(通过在一秒钟内多次单击蓝色下载区域)。结果是所有项目都被下载(除了上面提到的 404 错误)。

这是成功下载的屏幕截图(同样,404 除外)。此屏幕截图正在运行测试用例 #2(HttpClient 的多个实例)。我确实运行了测试用例 #1(HttpClient 的单个实例),结果也成功了。

测试应用截图

简而言之,我没有看到您遇到的问题。我唯一能想到的就是让您从不同的机器或位置尝试您的应用程序。

于 2013-01-09T19:24:45.957 回答