C#异步WebRequests:OnComplete事件

时间:2011-06-01 16:56:32

标签: c# asynchronous httpwebrequest

以下异步C#代码通过7个URL列表运行,并尝试从每个URL获取HTML。现在我只是将它输出到控制台的简单调试响应,如“站点HTML”,“无响应”或“错误的URL”。它似乎工作正常,但我需要在所有7个查询完成后触发一个事件。我该怎么做?重要的是要考虑所有案例:1)已收到网站HTML,2)网站超时,3)网站是一个不正确的网址,无法加载。我已经涵盖了所有这些案例,但无法弄清楚如何连接所有内容以触发全局“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 :(得分:0)

恕我直言创建信号量的最简单方法是,在主线程中将每个OnComplete处理程序设置为ReleaseWaitOne 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();
     }
 }

另一个选择是将一些WaitHandleManualResetEvent适合)传递给每个处理程序,并将WaitHandle.WaitAll传递给主线程中的<{p>}。

    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来实现相同的目标。 ExchangeCompareExchange方法,但恕我直言,WaitHandle在这里更直接(使用它们的性能不高)。

答案 1 :(得分:0)

您可以使用CountdownEvent查找所有网站的扫描时间。它最初设置为sites.Count,然后等待该事件。在每次完成时(通过错误,超时或成功),您都会发出事件信号。当事件计数达到零时,等待将返回,您可以进行“OnComplete”事件。