开发者

C# Async WebRequests: OnComplete Event

The following asynchronous C# code runs through a list of 7 URLs and tries to get HTML from each one. Right now I just have it outputting simple debug responses to the console like, "Site HTML", "No Response", or "Bad URL". It seems to work fine, but I need to fire off an event once all 7 queries have been made. How would I do this? It's important that all cases are taken into account: 1) Site HTML has been received, 2) Site timed out, 3) Site was an improper URL and couldn't be loaded. I'm covering all these cases already, but can't figure out how to connect everything to trigger a global "OnComplete" event.

Thank you.

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)
               开发者_如何学C     {
                        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;
        }
    }
}


You can use a CountdownEvent to find out when all the sites have been scanned. It'd be initially set to sites.Count, and then wait on that event. On each completion (either by error, timeout or success) you'd signal the event. When the event count reaches zero, the wait will return and you can have your "OnComplete" event.


The simplest way IMHO is to create a semaphore, make each OnComplete handler to Release it and to WaitOne on it N times in a master thread (where N is number of sites).

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

Another option is to pass some WaitHandle (ManualResetEvent fits well) to each of handler and WaitHandle.WaitAll for them in master thread.

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

Surely, you can achieve the same with Interlocked as well, by using, e.g. Exchange or CompareExchange methods but IMHO, WaitHandles are more straightforward here (and performance hit for using them is not significant).

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜