2011-06-01 24 views
0

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

回答

0

您可以使用CountdownEvent找出何时扫描了所有网站。它最初将设置为sites.Count,然后等待该事件。在每次完成时(无论是错误,超时还是成功),你都会发出信号。当事件计数达到零时,等待将返回并且您可以有“OnComplete”事件。

+0

请示例代码? – liquidgraph 2011-06-01 17:39:16

+0

添加“static CountdownEvent evt;”向班级申报;在调用ScanSites之前初始化它“evt = new CountdownEvent(_total);”;在调用ScanSites之后,添加“evt.Wait()”。最后,在扫描特定站点的每个地方完成(ScanSites的foreach循环中的catch块,ResponseCallback的结束),您会调用“evt.Signal()”。 – carlosfigueira 2011-06-01 17:57:40

+0

是的,这很好。我必须解决的最后一个问题是,尽管超时,某些站点仍挂起程序。我是否需要第二次超时(ThreadPool.RegisterWaitForSingleObject)进行异步读操作?我有一种感觉,这个刮板正在阅读非常非常长的HTML页面。 – liquidgraph 2011-06-01 21:22:54

0

最简单的方法是恕我直言创建信号,使每个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(); 
    } 
} 

另一种选择是通过一些WaitHandleManualResetEvent配合孔)到每个处理程序的和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来达到同样的效果。 ExchangeCompareExchange方法,但恕我直言,WaitHandle在这里更直截了当(和使用它们的性能不重要)。

+0

示例代码请 – liquidgraph 2011-06-01 17:39:26