The request was aborted: Could not create SSL/TLS

2019-08-10 20:48发布

问题:

I have a list of 350 downloadable image urls. I download 10 images parallely at one shot by running multiple tasks. But after downloading N number of images suddenly my code throws the following exception.

Exception: "An error occurred while sending the request."

InnerException: "The request was aborted: Could not create SSL/TLS secure channel."

StackTrace: "at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)\r\n at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)\r\n at System.Runtime.CompilerServices.TaskAwaiter`1.GetResult()\r\n ...

I have created a sample project to reproduce this exception. I have 2 test-cases in my hand. You can download the running test project from My Sky Drive Here. Right click on the file HTTPClientTestCases1and2.zip and download.

Case 1: Using a single instance HttpClient for all image Download.

In this case I'm sending parallel request to 10 urls using same HttpClient. In this case download is successful for most of the time. After last successful download of an image wait for minimum 40 seconds (Max 1 minute 40 seconds) to send the next parallel download request for the next batch. One image will definitely fail due to this exception. But so many places its written and suggested to use single HttpClient for multiple request.

   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);
        }
    }

Case 2: Creating new instance of HttpClient for every image Download

In this case it just fails very frequently due to same exception while downloading images parallely.

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);
    }
}

Please edit the following function in MainPage.xaml.cs to check two cases

 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);
            });
        }
    }

My Question: What I'm doing wrong? What is the best way of implementing parallel downloader in WinRT by overcoming this exception.

回答1:

I ran your sample application and am only get errors in a couple of scenarios:

  1. When the image that your app is requesting does not exist, the .NET HTTP client throws an exception. Your handler doesn't quite handle this case, as the inner exception is NULL. I had to tweak that code just a little:

    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. The only time I got your other error Could not create SSL/TLS secure channel in Windows 8 Metro App, is when I was using a HTTP debugging proxy (Fiddler). If I don't user Fiddler, which intercepts all HTTP(S) calls, then I have no problems downloading. I even started multiple downloads in rapid succession (by clicking the blue download area multiple times within one second). The result was that all items were downloaded (except for the 404 errors, as mentioned above).

Here is a screenshot of the successful downloads (again except for the 404s). This screenshot is running test case #2 (multiple instances of HttpClient). I did run test case #1 (single instance of HttpClient) and the results were also successful.

In short, I did not see the problems that you are experiencing. The only thing I can think of is for you to try your app from a different machine or location.