简体   繁体   中英

Async method doesn't work as expected

I am making test console app that is to download streaming mp4 using URL.

I used async example posted on MSDN.

Below is the code.

 class Program
{
    public static ManualResetEvent allDone = new ManualResetEvent(false);
    const int BUFFER_SIZE = 1024;
    public static int total = 0;

    static void Main(string[] args)
    {

        Uri httpSite = new Uri("URI");

        WebRequest wreq = WebRequest.Create(httpSite);

        RequestState rs = new RequestState();

        rs.Request = wreq;

        IAsyncResult r = (IAsyncResult)wreq.BeginGetResponse(
           new AsyncCallback(RespCallback), rs);

        allDone.WaitOne();
        FileStream file = new FileStream("test.mp4", FileMode.Append);
        file.Write(rs.data.ToArray(), 0, rs.data.Count());
        Console.ReadKey();

    }

    private static void RespCallback(IAsyncResult ar)
    {
        // Get the RequestState object from the async result.
        RequestState rs = (RequestState)ar.AsyncState;

        // Get the WebRequest from RequestState.
        WebRequest req = rs.Request;

        // Call EndGetResponse, which produces the WebResponse object
        //  that came from the request issued above.
        WebResponse resp = req.EndGetResponse(ar);

        //  Start reading data from the response stream.
        Stream ResponseStream = resp.GetResponseStream();

        // Store the response stream in RequestState to read 
        // the stream asynchronously.
        rs.ResponseStream = ResponseStream;

        //  Pass rs.BufferRead to BeginRead. Read data into rs.BufferRead
        IAsyncResult iarRead = ResponseStream.BeginRead(rs.BufferRead, 0,
           BUFFER_SIZE, new AsyncCallback(ReadCallBack), rs);
    }
    private static void ReadCallBack(IAsyncResult asyncResult)
    {
        // Get the RequestState object from AsyncResult.
        RequestState rs = (RequestState)asyncResult.AsyncState;

        // Retrieve the ResponseStream that was set in RespCallback. 
        Stream responseStream = rs.ResponseStream;

        // Read rs.BufferRead to verify that it contains data. 
        int read = responseStream.EndRead(asyncResult);
        if (read > 0)
        {
            // Prepare a Char array buffer for converting to Unicode.
            byte[] charBuffer = new byte[BUFFER_SIZE];             


            responseStream.Read(charBuffer, 0, read);
            rs.data.AddRange(charBuffer);


            IAsyncResult ar = responseStream.BeginRead(
               rs.BufferRead, 0, BUFFER_SIZE,
               new AsyncCallback(ReadCallBack), rs);
        }
        else
        {               
            responseStream.Close();
            // Set the ManualResetEvent so the main thread can exit.
            allDone.Set();
        }
        return;
    }
}

When I used this async method the saved file size is 1,356KB whele the file is suppoed to be 2,409KB.

I could find the breakpoint in IF(read > 0) statement was hit after the breakpoint on responseStream.close() was hit. Is this because it read 0 length and read more after then?

When I used non-async method like below, I could successfully download the file.

  public static void StreamDownload(string url)
    {
        int dataLength;
        int bytesRead;

        WebRequest req = WebRequest.Create(url);
        WebResponse response = req.GetResponse();
        string fileName = System.IO.Path.GetFileName("test.mp4");

        Stream dataStream = response.GetResponseStream();

        byte[] buffer = new byte[1024];

        FileStream file = new FileStream(fileName, FileMode.Append);

        dataLength = (int)response.ContentLength;

        do
        {
            bytesRead = dataStream.Read(buffer, 0, buffer.Length);
            file.Write(buffer, 0, bytesRead);

        } while (bytesRead != 0);


    }

Doesn anyone know why I failed with async method?

Thanks for your help.

There are multiple other ways to download a file that are far more simple , you should choose one of them.

webclient vs httpclient vs httpwebrequest

In your case I would suggest HttpClient :

public static async Task DownloadAsync(string requestUri, string filename)
{
    using (var httpClient = new HttpClient())
    {
        using (var fs = File.OpenWrite(filename))
        {
            await (await httpClient.GetStreamAsync(requestUri)).CopyToAsync(fs);
        }
    }
}

Or you could use the DownloadFileAsync method of the WebClient class, if you need to show some progress, but the example above is simpler.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM