Making an asynchronous WebRequest

From: http://holyhoehle.wordpress.com/2010/01/15/making-an-asynchronous-webrequest/

Making an asynchronous WebRequest

When downloading large amounts of data a normal synchronous web request, as described in the previous article, may block your current thread. The solution for this is to make an asynchronous web request. To start an asynchronous webrequest WebRequest.BeginGetResponse() has to be called. But before we can do that, we have to write some decent lines of code.

At first we have to create an object that stores the state of the request. To do this we create a new class RequestState:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
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 ;
     }
}

After that we need to create a Callback method to process the response. This has to be in the form of the AsyncCallback delegate:

1
public delegate void AsyncCallback(IAsyncResult ar);

So here’s our method:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
private void ResponseCallback(IAsyncResult result)
{
     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();
     }
}

As you can see, we need another Callback method for our asynchronous read operation. We also just could call the responseStream.ReadToEnd() method to get the response data but that would again block the calling thread. So the response should also be read asynchonously.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
private 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)
             {
                 // 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();
     }
}

Now that we have written all this code, we are finally ready to call WebRequest.BeginGetResponse() and pass in an instance of the RequestState class and the ResponseCallback:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
public void MakeWebRequestAsync( string url)
{
     try
     {
         HttpWebRequest request = (HttpWebRequest)WebRequest.Create(url);
         request.Method = "GET" ;
         request.Proxy = null ;
 
         RequestState state = new RequestState();
         state.Request = request;
 
         IAsyncResult result = request.BeginGetResponse( new AsyncCallback(ResponseCallback), state);
     }
     catch (Exception ex)
     {
         // Error handling
     }
}

Implementing a timeout functionality

When making an asynchronous webrequest the WebRequest.Timeout property will be ignored. So we have to implement our own timeout machanism. The following lines will demonstrate how to accomplish that task.

First we need to register a timeout delegate after the request.BeginGetResponse() call:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
public void MakeWebRequestAsync( string url, int timeOut)
{
     try
     {
         HttpWebRequest request = (HttpWebRequest)WebRequest.Create(url);
         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, timeOut, true );
     }
     catch (Exception ex)
     {
         // Error handling
     }
}

In the TimeOutCallback we’re just aborting the webrequest. It looks something like this:

1
2
3
4
5
6
7
8
9
private void TimeOutCallback( object state, bool timedOut)
{
     if (timedOut)
     {
         HttpWebRequest request = state as HttpWebRequest;
         if (request != null )
             request.Abort();
     }
}

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值