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:


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:


    public delegate void AsyncCallback(IAsyncResult ar);

So here’s our method:


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.


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:


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:

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:


private 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; } }
Advertisements

9 Responses to “Making an asynchronous WebRequest”

  1. Rick Hubka Says:

    Hi Steffen

    Thanks for posting this. It is great stuff!

    I am having 2 problems with the code though.
    In the ReadCallback method
    AsyncResponseContent
    and
    OnAsyncResponseArrived(AsyncResponseContent)
    both give errors.
    There seems to be some code missing.

    Thanks
    Rick

  2. Rick Hubka Says:

    Sorry

    Never mind. I was having a blond moment.
    I added the word string before AsyncResponseContent
    Then created my own OnAsyncResponseArrived method to pass it. Then use OnAsyncResponseArrived to do whatever.

    Cheers…

  3. Steffen Höhle Says:

    Hi Rick,
    thanks for your comment.
    As you’ve already noticed the AsyncResponseContent and OnAsyncResponseArrived(AsyncResponseContent)
    are just placeholders for a property or a method or whatever logic that could be called there.
    I’ve omitted the implementation since it’s not relevant for the WebRequest itself.

    Best regards,
    Steffen

  4. Dave Says:

    Thanks, this was very helpful!

  5. smith Says:

    Hello,

    Am confused is the whole code part of a class or the other parts can exist in a form?

    thanks

  6. Amrendra Kumar Says:

    And how to use EndGetResponse, and take the response and process the stream if the response is in Json format
    I will be thank full to for solving my problem

  7. Ekaterina Says:

    Hi. Thank you for your post. It is very helpful.
    But I have an exception accessing visual controls in OnAsyncResponseArrived method.
    I’d appreciate for giving me a tip about solving this problem

  8. avaya Says:

    I am having problem in understanding the example. When I tried below example It didn’t work.
    1> Class A calls Class B’s execute method.
    2> Class B’s execute method returns a custom object which can be later populated by callback method.
    3> When I go with async req.BeginGetResponse(new AsyncCallback(RespCallback), rs);
    rs is the request state object which hold the return object and will be populated on by RespCallback method.

    When I try this it doe not wait for response and returns to Class A’s calling method as expected. But the problem is that it never call the callback method with this approach. But if i have manual notification using WaitOne() it works.

    code
    =========
    Class A{
    public void execute() {
    B b = new B();
    Res rs = b.execute();
    Console.Write(rs);
    }
    }
    Class B{
    public Res execute() {
    WebRequest req = WebRequest.Create(finalURL);
    Res rs = new Res();
    req.BeginGetResponse(
    new AsyncCallback(RespCallback), rs);
    return rs;
    }
    private static void RespCallback(IAsyncResult ar){
    Res rs = (Res)ar.AsyncState;
    }
    }

  9. dddd Says:

    you can directly use
    “DownloadFileAsync” of the WebClient class. no need to type a lot of codes.


Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: