Невозможно выполнить второй запрос WebClient после истечения времени ожидания и отмены запроса

У меня есть настольное приложение, которое загружает 1 или более небольших файлов (jpg размером менее 400 КБ и не более 20 одновременно), используя объект CustomWebClient и вызывая OpenReadAsync(). Процесс загрузки работает нормально, если в процессе нет проблем. Я хочу ограничить ответ определенным временем (15 секунд), поэтому я ввел обработку timeOut, которая отменяет запрос. Даже время ожидания работает, и после этого мой метод "OpenReadCompletedEven tHandler" получает System.Net.WebException: запрос был прерван: запрос был отменен (что является правильным поведением). Теперь моя проблема заключается в том, что я хочу разрешить пользователю повторно загружать изображения. Таким образом, следующий запрос (запросы) webClient завершится с тем же исключением WebException. Ниже мой код.

Вот мой класс Custom WebClient (используется для того, чтобы иметь более 2 асинхронных подключений одновременно):

internal class ExtendedWebClient : WebClient
{
    private Timer _timer;
    public int ConnectionLimit { get; set; }
    public int ConnectionTimeout { get; set; }
    public ExtendedWebClient()
    {
        this.ConnectionLimit = 2;
    }

    protected override WebRequest GetWebRequest(Uri address)
    {
        var request = base.GetWebRequest(address) as HttpWebRequest;

        if (request != null){_timer = new Timer(TimeoutRequest, request, ConnectionTimeout, Timeout.Infinite);

            request.ServicePoint.ConnectionLimit = this.ConnectionLimit;
            request.ServicePoint.MaxIdleTime = 5000;
            request.ServicePoint.ConnectionLeaseTimeout = 5000;
        }

        return request;
    }

    private void TimeoutRequest(object state)
    {
        _timer.Dispose();
        _timer = null;
        ((WebRequest)state).Abort();
    }

    protected override void Dispose(bool disposing)
    {
        if (_timer != null)
        {
            _timer.Dispose();
            _timer = null;
        }
        base.Dispose(disposing);
    }    
}

Вот код для загрузки файлов с использованием моего пользовательского класса WebClient:

internal struct PageWaitHandleState
{
    public int WaitHandleIndexInPage;
    public bool ImageIsLoaded;
    public string ErrMessage;
}

public Image[] downloadedImages;

private PageWaitHandleState[] waitHandlesInPage;
private OpenReadCompletedEventHandler[] downloadComplete;
private EventWaitHandle[] pagesEWH = null;
private EventWaitHandle[] downloadImageEvent;
private int availableImages = 1;  // Set here to simplify, but as I stated in my description, it may be more than 1.
int downloadTimeOut = 15000;
int maxSimultaneousDownloads = 20;

private void DownloadImages(int pageIndex = 0, string[] imageUrl)
{
    if (pagesEWH[pageIndex] != null)
    {
        ReloadImages(pageIndex, imageUrl);  // Executed in the second request
        return;
    else
    {
        pagesEWH[pageIndex] = new EventWaitHandle[availableImages]; 
        downloadedImages = new Image[availableImages];
        downloadComplete = new OpenReadCompletedEventHandler[availableImages];
        downloadImageEvent = new EventWaitHandle[availableImages];
        waitHandlesInPage = new PageWaitHandleState[availableImages];

        // Set the downloadComplete deletages
        for (int i = 0; i < availableImages; i++)
        {
            downloadComplete[i] = ProcessImage;
        }
    }

    for (int imgCounter = 0; i < availableImages; i++)
    {
        waitHandlesInPage[imgCounter] = new PageWaitHandleState() { ImageIsLoaded = false, WaitHandleIndexInPage = imgCounter, ErrMessage = null };
        downloadImageEvent[imgCounter] = GrabImageAsync(imageUrl[imgCounter], downloadComplete[imgCounter], imgCounter, downloadTimeOut, maxSimultaneousDownloads);
        pagesEWH[imgCounter] = downloadImageEvent[imgCounter];
    }

        offenderIndex++;
    }
}

private static EventWaitHandle GrabImageAsync(string url, OpenReadCompletedEventHandler openReadCompletedEventHandler, int imgCounter, int downloadTimeOut, int maxSimultaneousDownloads)
{
    var myClient = new ExtendedWebClient();
    myClient.ConnectionLimit = maxSimultaneousDownloads;
    myClient.ConnectionTimeout = downloadTimeOut;
    myClient.OpenReadCompleted += openReadCompletedEventHandler;
    var iewh = new ImageEventWaitHandle() { ewh = new EventWaitHandle(false, EventResetMode.ManualReset), ImageIndex = imgCounter };
    myClient.OpenReadAsync(new Uri(url), iewh);
    return iewh.ewh;
}

internal void ProcessImage(object sender, OpenReadCompletedEventArgs e)
{
    ImageEventWaitHandle iewh = (ImageEventWaitHandle)e.UserState;
    bool disposeObject = false;

    try
    {
        if (e.Cancelled)
        {
            this.waitHandlesInPage[iewh.ImageIndex].ImageIsLoaded = false;
            this.waitHandlesInPage[iewh.ImageIndex].ErrMessage = "WebClient request was cancelled";
        }
        else if (e.Error != null)
        {
            this.waitHandlesInPage[iewh.ImageIndex].ImageIsLoaded = false;
            this.waitHandlesInPage[iewh.ImageIndex].ErrMessage = e.Error.Message;
            iewh.ewh.Set();
            this.downloadImageEvent[iewh.ImageIndex].Close();
        }
        else
        {
            using (Stream inputStream = e.Result)
            using (MemoryStream ms = new MemoryStream())
            {
                byte[] buffer = new byte[4096];
                int bytesRead;
                int totalReadBytes = 0;
                do
                {
                    bytesRead = inputStream.Read(buffer, 0, buffer.Length); // Exception fired here with the second request
                    ms.Write(buffer, 0, bytesRead);
                    totalReadBytes += bytesRead;
                } while (inputStream.CanRead && bytesRead > 0); 

                this.downloadedImages[iewh.ImageIndex] = Image.FromStream(ms);
                this.waitHandlesInPage[iewh.ImageIndex].ImageIsLoaded = true;
                this.waitHandlesInPage[iewh.ImageIndex].ErrMessage = null;
            }
            disposeObject = true;
        }
    }
    catch (Exception exc)
    {
        this.downloadedImages[iewh.ImageIndex] = null;
    }
    finally
    {
        // Signal the wait handle
        if (disposeObject)
        {
            iewh.ewh.Set();
            ((WebClient)sender).Dispose();
        }
    }
}

private void ReloadImages(int pageIndex, string[] imageUrl)
{
    for (int imgCounter = 0; imgCounter < availableImages; imgCounter++)
    {
        this.downloadComplete[imgCounter] = this.ProcessImage;
        this.waitHandlesInPage[imgCounter] = new PageWaitHandleState() { ImageIsLoaded = false, WaitHandleIndexInPage = imgCounter, ErrMessage = null };
        this.downloadImageEvent[imgCounter] = GrabImageAsync(ImageUrl[imgCounter],this.downloadComplete[imgCounter], imgCounter, downloadTimeOut, maxSimultaneousDownloads);
        this.pagesEWH[imgCounter] = this.downloadImageEvent[imgCounter];
    }
}

Наконец, когда я хочу получить доступ к изображениям, я проверяю, готовы ли они, используя:

private bool ImagesInPageReady(int pageIndex, int recordsInCurrentPage)
{
    if (_PagesEWH[pageIndex] != null)
    {
        int completedDownloadsCount = 0;
        bool waitHandleSet;

        // Wait for the default images first (imgCounter = 0). When moving page or asking for more pictures, then wait for the others.
        for (int ewhIndexInPage = 0; ewhIndexInPage < recordsInCurrentPage; ewhIndexInPage++)
        {
            if (this.pagesEWH[ewhIndexInPage].WaitOne(this.downloadTimeOut))
            {
                if (this.WaitHandlesInPage[ewhIndexInPage].ImageIsLoaded)
                {
                    completedDownloadsCount++;
                }
            }
            else
            {
                this.pagesEWH[ewhIndexInPage].Set();
            }
        }

        return (completedDownloadsCount > 0);
    }

    return false;
}

1 ответ

@usr, спасибо, что указал мне правильное направление. HttpClient был решением. Таким образом, я в основном инкапсулировал свой объект HttpClient в новый класс вместе с методом ProcessImage() и выставлением и событием, инициируемым тем же методом.

Другие вопросы по тегам