C # Async WebRequests: событие OnComplete - PullRequest
0 голосов
/ 01 июня 2011

Следующий асинхронный код C # проходит через список из 7 URL-адресов и пытается получить HTML из каждого. Прямо сейчас я просто выводил на консоль простые отладочные ответы, такие как «Site HTML», «No Response» или «Bad URL». Кажется, это работает нормально, но мне нужно отключить событие, как только все 7 запросов были сделаны. Как бы я это сделал? Важно, чтобы были учтены все случаи: 1) HTML сайта получен, 2) Сайт истек, 3) Сайт имеет неправильный URL и не может быть загружен. Я уже рассмотрел все эти случаи, но не могу понять, как все соединить, чтобы вызвать глобальное событие "OnComplete".

Спасибо.

using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using System.Net;
using System.Threading;
using System.Timers;
using System.Collections.Concurrent;
using System.Diagnostics;


namespace AsyncApp_05
{
    class Program
    {
        static int _count = 0;
        static int _total = 0;

        static void Main(string[] args)
        {
            ArrayList alSites = new ArrayList();
            alSites.Add("http://www.google.com");
            alSites.Add("http://www.yahoo.com");
            alSites.Add("http://www.ebay.com");
            alSites.Add("http://www.aol.com");
            alSites.Add("http://www.bing.com");
            alSites.Add("adsfsdfsdfsdffd");
            alSites.Add("http://wwww.fjasjfejlajfl");
            alSites.Add("http://mundocinema.com/noticias/the-a-team-2/4237");
            alSites.Add("http://www.spmb.or.id/?p=64");
            alSites.Add("http://gprs-edge.ru/?p=3");
            alSites.Add("http://blog.tmu.edu.tw/MT/mt-comments.pl?entry_id=3141");

            _total = alSites.Count;
            //Console.WriteLine(_total);
            ScanSites(alSites);

            Console.Read();
        }



        private static void ScanSites(ArrayList sites)
        {
            foreach (string uriString in sites)
            {
                try
                {
                    HttpWebRequest request = (HttpWebRequest)WebRequest.Create(uriString);
                    request.Method = "GET";
                    request.Proxy = null;

                    RequestState state = new RequestState();
                    state.Request = request;

                    IAsyncResult result = request.BeginGetResponse(new AsyncCallback(ResponseCallback), state);

                    // Timeout comes here
                    ThreadPool.RegisterWaitForSingleObject(result.AsyncWaitHandle,
                        new WaitOrTimerCallback(TimeOutCallback), request, 100, true);
                }
                catch (Exception ex)
                {
                    Console.WriteLine("Bad URL");
                    Interlocked.Increment(ref _count);
                }

            }
        }



        static void ReadCallback(IAsyncResult result)
        {
            try
            {
                // Get RequestState
                RequestState state = (RequestState)result.AsyncState;
                // determine how many bytes have been read
                int bytesRead = state.ResponseStream.EndRead(result);

                if (bytesRead > 0) // stream has not reached the end yet
                {
                    // append the read data to the ResponseContent and...
                    state.ResponseContent.Append(Encoding.ASCII.GetString(state.BufferRead, 0, bytesRead));
                    // ...read the next piece of data from the stream
                    state.ResponseStream.BeginRead(state.BufferRead, 0, state.BufferSize,
                        new AsyncCallback(ReadCallback), state);
                }
                else // end of the stream reached
                {
                    if (state.ResponseContent.Length > 0)
                    {
                        Console.WriteLine("Site HTML");
                        // do something with the response content, e.g. fill a property or fire an event
                        //AsyncResponseContent = state.ResponseContent.ToString();
                        // close the stream and the response
                        state.ResponseStream.Close();
                        state.Response.Close();
                        //OnAsyncResponseArrived(AsyncResponseContent);
                    }
                }
            }
            catch (Exception ex)
            {
                // Error handling
                RequestState state = (RequestState)result.AsyncState;
                if (state.Response != null)
                {
                    state.Response.Close();
                }
            }
        }


        static void ResponseCallback(IAsyncResult result)
        {
            Interlocked.Increment(ref _count);
            Console.WriteLine("Count: " + _count);
            try
            {
                // Get and fill the RequestState
                RequestState state = (RequestState)result.AsyncState;
                HttpWebRequest request = state.Request;
                // End the Asynchronous response and get the actual resonse object
                state.Response = (HttpWebResponse)request.EndGetResponse(result);
                Stream responseStream = state.Response.GetResponseStream();
                state.ResponseStream = responseStream;

                // Begin async reading of the contents
                IAsyncResult readResult = responseStream.BeginRead(state.BufferRead, 0, state.BufferSize, new AsyncCallback(ReadCallback), state);
            }
            catch (Exception ex)
            {
                // Error handling
                RequestState state = (RequestState)result.AsyncState;
                if (state.Response != null)
                {
                    state.Response.Close();
                }
                Console.WriteLine("No Response");
            }
        }


        static void TimeOutCallback(object state, bool timedOut)
        {
            if (timedOut)
            {
                HttpWebRequest request = state as HttpWebRequest;
                if (request != null)
                {
                    request.Abort();
                }
            }
        }


    }

    public class RequestState
    {
        public int BufferSize { get; private set; }
        public StringBuilder ResponseContent { get; set; }
        public byte[] BufferRead { get; set; }
        public HttpWebRequest Request { get; set; }
        public HttpWebResponse Response { get; set; }
        public Stream ResponseStream { get; set; }

        public RequestState()
        {
            BufferSize = 1024;
            BufferRead = new byte[BufferSize];
            ResponseContent = new StringBuilder();
            Request = null;
            ResponseStream = null;
        }
    }
}

Ответы [ 2 ]

0 голосов
/ 01 июня 2011

Вы можете использовать CountdownEvent , чтобы узнать, когда все сайты были отсканированы. Сначала он будет установлен на sites.Count, а затем будет ждать этого события. При каждом завершении (по ошибке, тайм-ауту или успеху) вы будете сигнализировать о событии. Когда количество событий достигнет нуля, ожидание вернется, и вы сможете получить событие «OnComplete».

0 голосов
/ 01 июня 2011

Самый простой способ IMHO - создать семафор, сделать каждый обработчик OnComplete для Release его и WaitOne для него N раз в главном потоке (где N - количество сайтов).

    private static void ScanSites(ArrayList sites)
    {
        var semaphore = new Semaphore(0,sites.Count);
        foreach (string uriString in sites)
        {
            try
            {
                HttpWebRequest request = (HttpWebRequest)WebRequest.Create(uriString);
                request.Method = "GET";
                request.Proxy = null;

                RequestState state = new RequestState();
                state.Request = request;
                state.Semaphore = semaphore;

                IAsyncResult result = request.BeginGetResponse(new AsyncCallback(ResponseCallback), state);

                // Timeout comes here
                ThreadPool.RegisterWaitForSingleObject(result.AsyncWaitHandle,
                    (o, timeout => { TimeOutCallback }, request, 100, true);
            }
            catch (Exception ex)
            {
                Console.WriteLine("Bad URL");
                Interlocked.Increment(ref _count);
            }
        }
     for(var i =0; i <sites.Count; i++) semaphore.WaitOne();
 }
 static void ReadCallback(IAsyncResult result)
 {
     try
         { ... }
     finally{
         var state = result.State as RequestState;
         if (state != null) state.Semaphore.Release();
     }
 }

Другой вариант - передать немного WaitHandle (ManualResetEvent хорошо подходит) каждому из обработчиков и WaitHandle.WaitAll для них в главном потоке.

    private static void ScanSites(ArrayList sites)
    {
        var handles = new List<WaitHandle>();
        foreach (string uriString in sites)
        {
            try
            {
                HttpWebRequest request = (HttpWebRequest)WebRequest.Create(uriString);
                request.Method = "GET";
                request.Proxy = null;

                RequestState state = new RequestState();
                state.Request = request;

                IAsyncResult result = request.BeginGetResponse(new AsyncCallback(ResponseCallback), state);
                handles.Add(result.AsyncWaitHandle);

                // Timeout comes here
                ThreadPool.RegisterWaitForSingleObject(result.AsyncWaitHandle,
                    new WaitOrTimerCallback(TimeOutCallback), request, 100, true);
            }
            catch (Exception ex)
            {
                Console.WriteLine("Bad URL");
                Interlocked.Increment(ref _count);
            }

        }
        WaitHandle.WaitAll(handles.ToArray());
    }

Конечно, вы можете достичьТо же самое и с Interlocked, используя, например, методы Exchange или CompareExchange, но, по-моему, WaitHandle здесь более просты (и снижение производительности при их использовании незначительно).

...