2011-06-01 62 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