发出异步请求Making Asynchronous Requests

System.Net 类为异步访问 Internet 资源使用 .NET Framework 的标准异步编程模型。The System.Net classes use the .NET Framework's standard asynchronous programming model for asynchronous access to Internet resources. WebRequest 类的 BeginGetResponseEndGetResponse 方法启动和完成 Internet 资源的异步请求。The BeginGetResponse and EndGetResponse methods of the WebRequest class start and complete asynchronous requests for an Internet resource.

备注

在异步回调方法中使用同步调用会导致严重的性能损失。Using synchronous calls in asynchronous callback methods can result in severe performance penalties. 用 WebRequest 提出的 Internet 请求及其后代必须使用 Stream.BeginRead 读取 WebResponse.GetResponseStream 方法返回的流。Internet requests made with WebRequest and its descendants must use Stream.BeginRead to read the stream returned by the WebResponse.GetResponseStream method.

以下示例代码演示如何将异步调用与 WebRequest 类一起使用。The following sample code demonstrates how to use asynchronous calls with the WebRequest class. 此示例是一个控制台程序从命令行获取 URI,请求位于 URI 处的资源,然后打印控制台的数据(数据是从 Internet 接收的)。The sample is a console program that takes a URI from the command line, requests the resource at the URI, and then prints data to the console as it is received from the Internet.

程序定义了两个类以供自己使用:RequestState 和 ClientGetAsync,前者跨异步调用传递数据,后者实现对 Internet 资源的异步请求 。The program defines two classes for its own use, the RequestState class, which passes data across asynchronous calls, and the ClientGetAsync class, which implements the asynchronous request to an Internet resource.

RequestState 类跨异步方法(这些方法为请求提供服务)调用,保留请求的状态。The RequestState class preserves the state of the request across calls to the asynchronous methods that service the request. 它包含 WebRequest 和 Stream 实例(含对资源的当前请求和在响应中接收的数据流)、一个缓冲区(含当前从 Internet 资源接收的数据)和一个 StringBuilder 实例(含完整响应)。 It contains WebRequest and Stream instances that contain the current request to the resource and the stream received in response, a buffer that contains the data currently received from the Internet resource, and a StringBuilder that contains the complete response. 使用 WebRequest.BeginGetResponse 注册了 AsyncCallback 方法时,将 RequestState 作为状态参数传递。 A RequestState is passed as the state parameter when the AsyncCallback method is registered with WebRequest.BeginGetResponse.

ClientGetAsync 类实现 Internet 资源的异步请求,并将所得的响应写入控制台。The ClientGetAsync class implements an asynchronous request to an Internet resource and writes the resulting response to the console. 它包含以下列表中描述的方法和属性。It contains the methods and properties described in the following list.

  • allDone 属性包含 ManualResetEvent 类的实例,它指示请求完成。The allDone property contains an instance of the ManualResetEvent class that signals the completion of the request.

  • Main() 方法读取命令行并启动对指定 Internet 资源的请求。The Main() method reads the command line and begins the request for the specified Internet resource. 它创建 WebRequest wreq 和 RequestState rs,调用 BeginGetResponse 开始处理请求,然后调用 allDone.WaitOne() 方法,以便应用程序不会在回调完成前退出。 It creates the WebRequest wreq and the RequestState rs, calls BeginGetResponse to begin processing the request, and then calls the allDone.WaitOne()method so that the application will not exit until the callback is complete. 在从 Internet 资源读取响应后,Main() 将该响应写入到控制台,应用程序结束。After the response is read from the Internet resource, Main() writes it to the console and the application ends.

  • showusage() 方法在控制台上写入一个示例命令行。The showusage() method writes an example command line on the console. 命令行上未提供 URI 时,由 Main() 调用它。It is called by Main() when no URI is provided on the command line.

  • RespCallBack() 方法实现 Internet 请求的异步回调方法。The RespCallBack() method implements the asynchronous callback method for the Internet request. 该方法创建包含来自 Internet 资源的响应的 WebResponse 实例,获取响应流,然后开始从该流异步读取数据。It creates the WebResponse instance containing the response from the Internet resource, gets the response stream, and then starts reading the data from the stream asynchronously.

  • ReadCallBack() 方法实现用于读取响应流的异步调用方法。The ReadCallBack() method implements the asynchronous callback method for reading the response stream. 它将从 Internet 资源接收到的数据传输到 RequestState 实例的 ResponseData 属性中,然后开始对响应流的另一次异步读取,直到不再返回数据。It transfers data received from the Internet resource into the ResponseData property of the RequestState instance, then starts another asynchronous read of the response stream until no more data is returned. 已读取全部数据后,ReadCallBack() 关闭响应流并调用 allDone.Set() 方法,指示整个响应存在于 ResponseData 中 。Once all the data has been read, ReadCallBack() closes the response stream and calls the allDone.Set() method to indicate that the entire response is present in ResponseData.

    备注

    所有网络流均已关闭,这一点非常重要。It is critical that all network streams are closed. 如果未关闭每个请求和响应流,应用程序将耗尽与服务器的连接,无法处理其他请求。If you do not close each request and response stream, your application will run out of connections to the server and be unable to process additional requests.

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;  
   }      
}  
Imports System  
Imports System.Net  
Imports System.Threading  
Imports System.Text  
Imports System.IO  
  
' The RequestState class passes data across async calls.  
Public Class RequestState  
  
      Public RequestData As New StringBuilder("")  
      Public BufferRead(1024) As Byte  
      Public Request As HttpWebRequest  
      Public ResponseStream As Stream  
      ' Create Decoder for appropriate encoding type.  
      Public StreamDecode As Decoder = Encoding.UTF8.GetDecoder()  
  
      Public Sub New  
         Request = Nothing  
         ResponseStream = Nothing  
      End Sub  
End Class  
  
' ClientGetAsync issues the async request.  
Class ClientGetAsync  
   Shared allDone As New ManualResetEvent(False)  
      Const BUFFER_SIZE As Integer = 1024  
  
    Shared Sub Main()  
        Dim Args As String() = Environment.GetCommandLineArgs()  
  
        If Args.Length < 2 Then  
            ShowUsage()  
            Return  
        End If  
  
      ' Get the URI from the command line.  
        Dim HttpSite As Uri = New Uri(Args(1))  
  
      ' Create the request object.  
        Dim wreq As HttpWebRequest = _  
           CType(WebRequest.Create(HttpSite), HttpWebRequest)  
  
      ' Create the state object.  
      Dim rs As RequestState = New RequestState()  
  
      ' Put the request into the state so it can be passed around.  
      rs.Request = wreq  
  
      ' Issue the async request.  
        Dim r As IAsyncResult = _  
           CType(wreq.BeginGetResponse( _  
           New AsyncCallback(AddressOf RespCallback), rs), IAsyncResult)  
  
      ' Wait until the ManualResetEvent is set so that the application  
      ' does not exit until after the callback is called.  
        allDone.WaitOne()  
    End Sub  
  
    Shared Sub ShowUsage()  
        Console.WriteLine("Attempts to GET a URI")  
        Console.WriteLine()  
        Console.WriteLine("Usage:")  
        Console.WriteLine("ClientGetAsync URI")  
        Console.WriteLine("Examples:")  
        Console.WriteLine("ClientGetAsync http://www.contoso.com/")  
    End Sub  
  
    Shared Sub RespCallback(ar As IAsyncResult)  
       ' Get the RequestState object from the async result.  
       Dim rs As RequestState = CType(ar.AsyncState, RequestState)  
  
       ' Get the HttpWebRequest from RequestState.  
       Dim req As HttpWebRequest= rs.Request  
  
       ' Call EndGetResponse, which returns the HttpWebResponse object  
       ' that came from the request issued above.  
       Dim resp As HttpWebResponse = _  
           CType(req.EndGetResponse(ar), HttpWebResponse)  
  
       ' Start reading data from the response stream.  
       Dim ResponseStream As Stream = resp.GetResponseStream()  
  
       ' Store the reponse stream in RequestState to read  
       ' the stream asynchronously.  
       rs.ResponseStream = ResponseStream  
  
       ' Pass rs.BufferRead to BeginRead. Read data into rs.BufferRead.  
       Dim iarRead As IAsyncResult = _  
          ResponseStream.BeginRead(rs.BufferRead, 0, BUFFER_SIZE, _  
          New AsyncCallback(AddressOf ReadCallBack), rs)  
    End Sub  
  
   Shared Sub ReadCallBack(asyncResult As IAsyncResult)  
      ' Get the RequestState object from the AsyncResult.  
      Dim rs As RequestState = CType(asyncResult.AsyncState, RequestState)  
  
      ' Retrieve the ResponseStream that was set in RespCallback.  
      Dim responseStream As Stream = rs.ResponseStream  
  
      ' Read rs.BufferRead to verify that it contains data.  
      Dim read As Integer = responseStream.EndRead( asyncResult )  
      If read > 0 Then  
         ' Prepare a Char array buffer for converting to Unicode.  
         Dim charBuffer(1024) As Char  
  
         ' Convert byte stream to Char array and then String.  
         ' len contains the number of characters converted to Unicode.  
         Dim len As Integer = _  
           rs.StreamDecode.GetChars(rs.BufferRead, 0, read, charBuffer, 0)  
         Dim str As String = 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.  
         Dim ar As IAsyncResult = _  
            responseStream.BeginRead(rs.BufferRead, 0, BUFFER_SIZE, _  
            New AsyncCallback(AddressOf ReadCallBack), rs)  
      Else  
          If rs.RequestData.Length > 1 Then  
            ' Display data to the console.  
            Dim strContent As String  
            strContent = rs.RequestData.ToString()  
            Console.WriteLine(strContent)  
         End If  
  
         ' Close down the response stream.  
         responseStream.Close()  
  
         ' Set the ManualResetEvent so the main thread can exit.  
         allDone.Set()  
      End If  
  
      Return  
   End Sub  
End Class  

请参阅See also