HttpWebRequest.BeginGetRequestStream()最佳实践

4
我正在开发一个异步Http爬虫,从各种服务中收集数据。目前,我正在使用线程池来进行串行的HttpWebRequest调用,以从服务中获取数据。
我想过渡到异步网络调用(BeginGetRequestStream和BeginGetResponse),我需要一些方法来获取响应数据和POST统计信息(写入完成的百分比,完成时机等)。我目前有一个事件,从生成/包含线程的对象中调用,表示已接收到HTTP数据。在WebRequests中是否有可以附加到已实现事件的事件?这将是过渡最无缝的方式。
感谢您的任何帮助!

哇,2500多次浏览却没有爱心?! - joe_coolish
4个回答

4

我刚刚从这篇文章中复制并编辑了以下代码,它与异步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对象替换为任何能帮助您跟踪事物的对象。
您可能已经在这样做了,但如果没有,我相信这是解决问题的一种完全可接受和干净的方式。如果这不是您要寻找的内容,请告诉我。

3
您可以通过传递一个委托(作为异步“状态”参数的一部分)来需要调用。然后在EndGetResponseStream之后做必要的事情,然后使用任何您需要的参数调用此委托。
个人建议,由于您正在转向aysnc编程模型(我假设是为了获得更好的性能),我强烈建议您将工作流程也转换为异步。这种模式允许您在结果到达并尽可能快地处理结果而不会发生任何阻塞。 编辑 我的博客上有一篇文章 HttpWebRequest - 异步编程模型/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));
        });
    }

循环可以是你的url集合。在GET请求的情况下,你不需要发送任何(POST)参数,回调函数是我写到控制台的lambda表达式。在这里,你可以做你需要做的事情,或者你可以发送一个委托,以便响应处理是在“其他地方”完成的。
此外,回调方法是一个
Action<HttpWebRequestCallbackState>

在这里,HttpWebRequestCallbackState 是一个自定义类,您可以修改它以包含您需要的任何信息。或者您也可以修改签名为 Action


2
您可以使用System.Net.WebClient类:
var client = new WebClient();
client.DownloadDataCompleted += (s, args) => { /* do stuff here */ };
client.DownloadDataAsync(new Uri("http://someuri.com/"));

哦,我忘了提到我可能需要支持紧凑框架,但如果不需要的话,我一定会使用WebClient。有没有适用于CF的友好WebClient? - joe_coolish
@Jimmer:对于CF来说,HttpWebRequest是唯一的方法。 - abatishchev

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;
            }

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接