HttpWebRequest.BeginGetRequestStream()最佳实践

时间:2011-02-23 23:12:11

标签: c# http httpwebrequest

我正在开发一个异步Http抓取工具,它从各种服务中收集数据,目前,我正在处理执行串行HttpWebRequest调用的线程池,以便从服务中发布/获取数据。

我想转换到异步Web调用(BeginGetRequestStream和BeginGetResponse),我需要某种方式获取响应数据和POST统计信息(%完成写入,完成时(完成时更重要)等)。我目前有一个从产生/包含线程的对象调用的事件,已收到信号HTTP数据。我可以附加的WebRequests中是否有一个事件来调用已经实现的事件?这对过渡来说是最无缝的。

感谢您的帮助!!

4 个答案:

答案 0 :(得分:4)

以下代码我只是从this article复制/粘贴(和编辑)有关异步Web请求的代码。它显示了如何以一种有条理的方式编写异步代码的基本模式,同时跟踪哪些响应与请求等有关。当您完成响应时,只需触发一个通知UI的事件回应完了。

private void ScanSites ()
{
  // for each URL in the collection...
  WebRequest request = HttpWebRequest.Create(uri);

  // RequestState is a custom class to pass info
  RequestState state = new RequestState(request, data);

  IAsyncResult result = request.BeginGetResponse(
    new AsyncCallback(UpdateItem),state);
}

private void UpdateItem (IAsyncResult result)
{
  // grab the custom state object
  RequestState state = (RequestState)result.AsyncState;
  WebRequest request = (WebRequest)state.request;
  // get the Response
  HttpWebResponse response =
    (HttpWebResponse )request.EndGetResponse(result);

  // fire the event that notifies the UI that data has been retrieved...
}

请注意,您可以将RequestState对象替换为您想要的任何类型的对象,以帮助您跟踪事物。

你可能已经这样做了,但如果没有,我相信这是一个完全可以接受和干净的方法来解决这个问题。如果这不是您想要的,请告诉我。

答案 1 :(得分:3)

您可以传递需要调用的委托(作为异步“状态”参数的一部分)。然后在您的EndGetResponseStream执行您需要的操作之后,然后使用您需要的任何参数调用此委托。

就个人而言,既然你正在转向aysnc编程模型(我假设要获得更好的性能),我强烈建议你将工作流程转移到异步。此模型允许您在结果进入时尽可能快地处理结果,而不会发生任何阻塞。

修改

在我的博客上有一篇文章

HttpWebRequest - Asynchronous Programming Model/Task.Factory.FromAsyc

关于这个问题。我目前正在编写它,但我已经提出了一个我认为你可以在你的情况下使用的课程。根据您的需要,查看GetAsync方法或PostAsync方法。

public static void GetAsyncTask(string url, Action<HttpWebRequestCallbackState> responseCallback,
  string contentType = "application/x-www-form-urlencoded")

注意responseCallback参数?那是我之前谈过的代表。

以下是您如何调用它的示例(我正在显示PostAsyn()方法

    var iterations = 100;
    for (int i = 0; i < iterations; i++)
    {
      var postParameters = new NameValueCollection();
      postParameters.Add("data", i.ToString());
      HttpSocket.PostAsync(url, postParameters, callbackState =>
        {
          if (callbackState.Exception != null)
            throw callbackState.Exception;
          Console.WriteLine(HttpSocket.GetResponseText(callbackState.ResponseStream));
        });
    }

循环可以是您的网址集合。在GET的情况下,您不需要发送任何(POST)参数,回调是您在我写入控制台的位置看到的lambda。在这里你可以做你需要的,你可以发送一个代表,以便响应处理在“其他地方”完成。

回调方法也是

Action<HttpWebRequestCallbackState>

HttpWebRequestCallbackState是自定义类,您可以修改以包含您出于此目的所需的任何信息。或者您可以将签名修改为Action。

答案 2 :(得分:2)

您可以使用System.Net.WebClient类:

var client = new WebClient();
client.DownloadDataCompleted += (s, args) => { /* do stuff here */ };
client.DownloadDataAsync(new Uri("http://someuri.com/"));

答案 3 :(得分:0)

第二种方法是我结束回应的主要方式。

            public string GetResponse()
            {
                // Get the original response.
                var response = _request.GetResponse();

                Status = ((HttpWebResponse) response).StatusDescription;

                // Get the stream containing all content returned by the requested server.
                _dataStream = response.GetResponseStream();

                // Open the stream using a StreamReader for easy access.
                var reader = new StreamReader(_dataStream);

                // Read the content fully up to the end.
                var responseFromServer = reader.ReadToEnd();

                // Clean up the streams.
                reader.Close();
                if (_dataStream != null) 
                    _dataStream.Close();

                response.Close();

                return responseFromServer;
            }

            /// <summary>
            /// Custom timeout on responses
            /// </summary>
            /// <param name="millisec"></param>
            /// <returns></returns>
            public string GetResponse(int millisec)
            {
                //Spin off a new thread that's safe for an ASP.NET application pool.
                var responseFromServer = "";
                var resetEvent = new ManualResetEvent(false);
                ThreadPool.QueueUserWorkItem(arg =>
                    {
                        try
                        {
                            responseFromServer = GetResponse();
                        }
                        catch (Exception ex)
                        {
                            throw ex;
                        }
                        finally
                        {
                            resetEvent.Set();//end of thread
                        }
                    });

                //handle a timeout with a asp.net thread safe method 
                WaitHandle.WaitAll(new WaitHandle[] { resetEvent }, millisec);
                return responseFromServer;
            }