Реализация асинхронных вызовов HttpWebRequest

у меня есть этот код

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

Мне нужно сделать его асинхронным. Насколько я понимаю, это означает, что мне нужно изменить вызов с request.GetRequestStream() на asychronous.BeginGetRequestStream(). Я видел этот пример, но не могу понять как применить это к этому сценарию. Кто-нибудь может помочь?


person Sachin Kainth    schedule 05.09.2012    source источник
comment
Какую версию .NET вы используете? Это тривиально с .NET 4.5.   -  person John Saunders    schedule 05.09.2012
comment
Это 4. Я пока не могу использовать 4.5.   -  person Sachin Kainth    schedule 05.09.2012
comment
Это все еще возможно в 4 правильно?   -  person Sachin Kainth    schedule 05.09.2012
comment
Да, только намного громоздче. В 4.5 это просто using (var stream = ...){await stream.WriteAsync(body.ToString());}   -  person John Saunders    schedule 05.09.2012
comment
Не могли бы вы помочь мне сделать это с 4?   -  person Sachin Kainth    schedule 05.09.2012
comment
Сегодня утром у меня нет времени, но подсказка: вы должны использовать Stream, а не StreamWriter.   -  person John Saunders    schedule 05.09.2012


Ответы (2)


В документации есть хороший пример (http://msdn.microsoft.com/en-us/library/system.net.httpwebrequest.begingetrequeststream%28v=vs.100%29.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();
}
person exacerbatedexpert    schedule 05.09.2012
comment
Как изменить этот код, чтобы он возвращал что-то (например, строку ответа) из GetResponseCallBack исходному вызывающему объекту, поскольку AsyncCallBack требует, чтобы подпись имела возвращаемый тип void? - person Prabu; 27.10.2014
comment
я понятия не имел, как это сделать, и этот ответ понятнее для меня, спасибо! - person Arturo Martinez; 15.03.2017

вы можете понять этот код.

Программа определяет два класса для собственного использования: класс RequestState, который передает данные через асинхронные вызовы, и класс ClientGetAsync, реализующий асинхронный запрос к интернет-ресурсу.

Класс RequestState сохраняет состояние запроса при вызовах асинхронных методов, обслуживающих запрос. Он содержит экземпляры WebRequest и Stream, содержащие текущий запрос к ресурсу и поток, полученный в ответ, буфер, содержащий данные, полученные в данный момент от интернет-ресурса, и StringBuilder, содержащий полный ответ. RequestStateis передается в качестве параметра состояния, когда метод AsyncCallback регистрируется в WebRequest.BeginGetResponse.

Класс ClientGetAsync реализует асинхронный запрос к интернет-ресурсу и записывает полученный ответ в консоль. Он содержит методы и свойства, описанные в следующем списке.

Свойство allDone содержит экземпляр класса ManualResetEvent, сигнализирующий о завершении запроса.

Метод Main() читает командную строку и начинает запрос указанного интернет-ресурса. Он создает WebRequest wreq и RequestState rs, вызывает BeginGetResponse, чтобы начать обработку запроса, а затем вызывает метод allDone.WaitOne(), чтобы приложение не завершило работу до завершения обратного вызова. После того как ответ прочитан с интернет-ресурса, Main() записывает его в консоль и приложение завершает работу.

Метод showusage() выводит на консоль пример командной строки. Он вызывается функцией Main(), когда в командной строке не указан URI.

Метод RespCallBack() реализует метод асинхронного обратного вызова для интернет-запроса. Он создает экземпляр WebResponse, содержащий ответ от интернет-ресурса, получает поток ответов, а затем начинает асинхронно считывать данные из потока.

Метод ReadCallBack() реализует метод асинхронного обратного вызова для чтения потока ответа. Он передает данные, полученные от интернет-ресурса, в свойство ResponseData экземпляра RequestState, затем запускает еще одно асинхронное чтение потока ответов до тех пор, пока данные не будут возвращены. Как только все данные будут прочитаны, 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;
   }    
}
person TechnicalKalsa    schedule 14.06.2014