1

350 個のダウンロード可能な画像 URL のリストがあります。複数のタスクを実行して、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 で。 GetResult()\r\n ...

この例外を再現するサンプル プロジェクトを作成しました。私は2つのテストケースを手にしています。My Sky Drive Hereから実行中のテスト プロジェクトをダウンロードできます。ファイル HTTPClientTestCases1and2.zip を右クリックしてダウンロードします。

ケース 1: すべてのイメージ ダウンロードに 1 つのインスタンス HttpClient を使用する。

この場合、同じ HttpClient を使用して 10 個の URL に並列リクエストを送信しています。この場合、ほとんどの場合、ダウンロードは成功します。イメージのダウンロードが最後に成功した後、次のバッチの次の並列ダウンロード要求を送信するために、最小 40 秒 (最大 1 分 40 秒) 待機します。この例外により、1 つのイメージは確実に失敗します。しかし、非常に多くの場所で、複数のリクエストに対して単一の 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 の次の関数を編集して、2 つのケースを確認してください。

 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 を使用しなければ、ダウンロードに問題はありません。複数のダウンロードを立て続けに開始することさえありました (青色のダウンロード領域を 1 秒以内に複数回クリックすることにより)。その結果、すべてのアイテムがダウンロードされました (上記の 404 エラーを除く)。

これは、成功したダウンロードのスクリーンショットです (これも 404 を除く)。このスクリーンショットは、テスト ケース #2 (HttpClient の複数のインスタンス) を実行しています。テスト ケース #1 (HttpClient の単一インスタンス) を実行しましたが、結果も成功しました。

テスト アプリケーションのスクリーンショット

要するに、私はあなたが経験している問題を見ませんでした。私が考えることができる唯一のことは、別のマシンまたは場所からアプリを試すことです.

于 2013-01-09T19:24:45.957 に答える