【问题标题】:Implementing HttpWebRequest Async calls实现 HttpWebRequest 异步调用
【发布时间】:2012-08-30 04:37:51
【问题描述】:

我有这个代码

using (var stream = new StreamWriter(request.GetRequestStream(), Encoding))
   stream.Write(body.ToString());

我需要让它异步。据我了解,这意味着我需要将对request.GetRequestStream() 的调用更改为asychronous.BeginGetRequestStream()。我看过this 示例,但无法弄清楚如何将其应用于此场景。有人可以帮忙吗?

【问题讨论】:

  • 您使用的是哪个版本的 .NET? .NET 4.5 很简单。
  • 现在是4。我还不能用4.5。
  • 4里还是可以的吧?
  • 是的,只是更麻烦。在 4.5 中,它只是 using (var stream = ...){await stream.WriteAsync(body.ToString());}
  • 你能帮我用 4 做吗?

标签: c# asynchronous httpwebrequest streamwriter


【解决方案1】:

你可以通过这段代码理解。

该程序定义了两个类供自己使用,RequestState 类通过异步调用传递数据,ClientGetAsync 类实现对 Internet 资源的异步请求。

RequestState 类在调用为请求提供服务的异步方法时保留请求的状态。它包含 WebRequest 和 Stream 实例,其中包含对资源的当前请求和响应中接收到的流,包含当前从 Internet 资源接收到的数据的缓冲区,以及包含完整响应的 StringBuilder。当向 WebRequest.BeginGetResponse 注册 AsyncCallback 方法时,将 RequestState 作为状态参数传递。

ClientGetAsync 类实现对 Internet 资源的异步请求,并将结果响应写入控制台。它包含以下列表中描述的方法和属性。

allDone 属性包含一个 ManualResetEvent 类的实例,该实例发出请求完成的信号。

Main() 方法读取命令行并开始对指定 Internet 资源的请求。它创建 WebRequest wreq 和 RequestState rs,调用 BeginGetResponse 开始处理请求,然后调用 allDone.WaitOne() 方法,以便应用程序在回调完成之前不会退出。从 Internet 资源读取响应后,Main() 将其写入控制台,应用程序结束。

showusage() 方法在控制台上编写示例命令行。当命令行没有提供 URI 时,由 Main() 调用。

RespCallBack() 方法实现了互联网请求的异步回调方法。它创建包含来自 Internet 资源的响应的 WebResponse 实例,获取响应流,然后开始异步读取流中的数据。

ReadCallBack() 方法实现了读取响应流的异步回调方法。它将从 Internet 资源接收到的数据传输到 RequestState 实例的 ResponseData 属性中,然后开始另一个异步读取响应流,直到不再返回数据。读取完所有数据后,ReadCallBack() 关闭响应流并调用 allDone.Set() 方法以指示整个响应存在于 ResponseData 中。

using System;
using System.Net;
using System.Threading;
using System.Text;
using System.IO;

// The RequestState class passes data across async calls.
public class RequestState
{
   const int BufferSize = 1024;
   public StringBuilder RequestData;
   public byte[] BufferRead;
   public WebRequest Request;
   public Stream ResponseStream;
   // Create Decoder for appropriate enconding type.
   public Decoder StreamDecode = Encoding.UTF8.GetDecoder();

   public RequestState()
   {
      BufferRead = new byte[BufferSize];
      RequestData = new StringBuilder(String.Empty);
      Request = null;
      ResponseStream = null;
   }     
}

// ClientGetAsync issues the async request.
class ClientGetAsync 
{
   public static ManualResetEvent allDone = new ManualResetEvent(false);
   const int BUFFER_SIZE = 1024;

   public static void Main(string[] args) 
   {
      if (args.Length < 1) 
      {
         showusage();
         return;
      }

      // Get the URI from the command line.
      Uri httpSite = new Uri(args[0]);

      // Create the request object.
      WebRequest wreq = WebRequest.Create(httpSite);

      // Create the state object.
      RequestState rs = new RequestState();

      // Put the request into the state object so it can be passed around.
      rs.Request = wreq;

      // Issue the async request.
      IAsyncResult r = (IAsyncResult) wreq.BeginGetResponse(
         new AsyncCallback(RespCallback), rs);

      // Wait until the ManualResetEvent is set so that the application 
      // does not exit until after the callback is called.
      allDone.WaitOne();

      Console.WriteLine(rs.RequestData.ToString());
   }

   public static void showusage() {
      Console.WriteLine("Attempts to GET a URL");
      Console.WriteLine("\r\nUsage:");
      Console.WriteLine("   ClientGetAsync URL");
      Console.WriteLine("   Example:");
      Console.WriteLine("      ClientGetAsync http://www.contoso.com/");
   }

   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.
         Char[] charBuffer = new Char[BUFFER_SIZE];

         // Convert byte stream to Char array and then to String.
         // len contains the number of characters converted to Unicode.
      int len = 
         rs.StreamDecode.GetChars(rs.BufferRead, 0, read, charBuffer, 0);

         String str = new String(charBuffer, 0, len);

         // Append the recently read data to the RequestData stringbuilder
         // object contained in RequestState.
         rs.RequestData.Append(
            Encoding.ASCII.GetString(rs.BufferRead, 0, read));         

         // Continue reading data until 
         // responseStream.EndRead returns –1.
         IAsyncResult ar = responseStream.BeginRead( 
            rs.BufferRead, 0, BUFFER_SIZE, 
            new AsyncCallback(ReadCallBack), rs);
      }
      else
      {
         if(rs.RequestData.Length>0)
         {
            //  Display data to the console.
            string strContent;                  
            strContent = rs.RequestData.ToString();
         }
         // Close down the response stream.
         responseStream.Close();         
         // Set the ManualResetEvent so the main thread can exit.
         allDone.Set();                           
      }
      return;
   }    
}

【讨论】:

    【解决方案2】:

    文档中有一个很好的例子 (http://msdn.microsoft.com/en-us/library/system.net.httpwebrequest.begingetrequeststream(v=vs.100).aspx):

    using System;
    using System.Net;
    using System.IO;
    using System.Text;
    using System.Threading;
    
    class HttpWebRequestBeginGetRequest
    {
        private static ManualResetEvent allDone = new ManualResetEvent(false);
    public static void Main(string[] args)
    {
        // Create a new HttpWebRequest object.
        HttpWebRequest request = (HttpWebRequest)WebRequest.Create("http://www.contoso.com/example.aspx");
    
        request.ContentType = "application/x-www-form-urlencoded";
    
        // Set the Method property to 'POST' to post data to the URI.
        request.Method = "POST";
    
        // start the asynchronous operation
        request.BeginGetRequestStream(new AsyncCallback(GetRequestStreamCallback), request);
    
        // Keep the main thread from continuing while the asynchronous 
        // operation completes. A real world application 
        // could do something useful such as updating its user interface. 
        allDone.WaitOne();
    }
    
    private static void GetRequestStreamCallback(IAsyncResult asynchronousResult)
    {
        HttpWebRequest request = (HttpWebRequest)asynchronousResult.AsyncState;
    
        // End the operation
        Stream postStream = request.EndGetRequestStream(asynchronousResult);
    
        Console.WriteLine("Please enter the input data to be posted:");
        string postData = Console.ReadLine();
    
        // Convert the string into a byte array. 
        byte[] byteArray = Encoding.UTF8.GetBytes(postData);
    
        // Write to the request stream.
        postStream.Write(byteArray, 0, postData.Length);
        postStream.Close();
    
        // Start the asynchronous operation to get the response
        request.BeginGetResponse(new AsyncCallback(GetResponseCallback), request);
    }
    
    private static void GetResponseCallback(IAsyncResult asynchronousResult)
    {
        HttpWebRequest request = (HttpWebRequest)asynchronousResult.AsyncState;
    
        // End the operation
        HttpWebResponse response = (HttpWebResponse)request.EndGetResponse(asynchronousResult);
        Stream streamResponse = response.GetResponseStream();
        StreamReader streamRead = new StreamReader(streamResponse);
        string responseString = streamRead.ReadToEnd();
        Console.WriteLine(responseString);
        // Close the stream object
        streamResponse.Close();
        streamRead.Close();
    
        // Release the HttpWebResponse
        response.Close();
        allDone.Set();
    }
    

    【讨论】:

    • 如何更改此代码以从 GetResponseCallBack 返回某些内容(例如响应字符串)给原始调用者,因为 AsyncCallBack 要求签名具有 void 返回类型?
    • 我不知道怎么做,这个答案对我来说更清楚,谢谢!
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-07-30
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多