C# - StreamReader.ReadToEnd()非常慢

时间:2015-02-03 10:10:05

标签: .net performance stream streamreader httpwebresponse

我正在创建一个Web Crawler,我发现我的一个方法GetHTML非常慢,因为它使用StreamReader从HttpWebResponse对象中获取HTML字符串。

以下是方法:

static string GetHTML(string URL)
      {
           HttpWebRequest Request = (HttpWebRequest)WebRequest.Create(URL);
           Request.Proxy = null;
           HttpWebResponse Response = ((HttpWebResponse)Request.GetResponse());
           Stream RespStream = Response.GetResponseStream();
           return new StreamReader(RespStream).ReadToEnd(); // Very slow
      }

我使用秒表进行了测试,并在YouTube上使用了此方法。

Time it takes to get an HTTP response: 500 MS

Time it takes to convert the HttpWebResponse object to a string: 550 MS

所以HTTP请求没问题,只是ReadToEnd()这么慢。

ReadToEnd()方法是否有任何替代方法可以从响应对象中获取HTML字符串?我尝试使用WebClient.DownloadString()方法,但它只是一个使用流的HttpWebRequest包装器。

编辑:使用套接字尝试它并且速度更快:

static string SocketHTML(string URL)
      {
           string IP = Dns.GetHostAddresses(URL)[0].ToString();
           Socket s = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
           s.Connect(new IPEndPoint(IPAddress.Parse(IP), 80));
           s.Send(Encoding.ASCII.GetBytes("GET / HTTP/1.1\r\n\r\n"));
           List<byte> HTML = new List<byte>();
           int Bytes = 1;
           while (Bytes > 0)
           {
                byte[] Data = new byte[1024];
                Bytes = s.Receive(Data);
                foreach (byte b in Data) HTML.Add(b);
           }
           s.Close();
           return Encoding.ASCII.GetString(HTML.ToArray());
      }

与套接字一起使用它的问题是,它大部分时间都会返回错误,例如“永久移动”或“您的浏览器发送了服务器无法理解的请求”。

3 个答案:

答案 0 :(得分:5)

  

当我调用此方法但返回String.Empty而不是ReadToEnd时,该方法大约需要500 MS。

所有这一切都是开始以获得响应需要500毫秒。调用GetResponseStream不会消耗所有数据。

ReadToEnd也将从二进制数据转换为文本,但我怀疑这是重要的 - 我强烈怀疑它只是等待数据通过网络到达。要验证这一点,您应该将日志记录添加到代码运行Wireshark的每个方面 - 然后您应该能够在数据到达时逐个数据包地查看,并将其与伐木。

作为一个副作用,你应该肯定有一个using的回复声明:

using (var response = ((HttpWebResponse)Request.GetResponse())
{
    // The stream will be disposed when the response is.
    return new StreamReader(response.GetResponseStream())
        .ReadToEnd();
}

如果您没有处理响应,您将绑定连接,直到垃圾收集器完成它们。这可能导致超时。

答案 1 :(得分:2)

  

我做了这个比较,看看StreamReader.ReadToEnd()是否是瓶颈,我已经看到了。

你在这里得出了错误的结论:瓶颈是整个方法,而不仅仅是StreamReader.ReadToEnd()部分。

  

当我收到回复并且我不使用ReadToEnd()方法时,大约需要500 MS,但如果我使用ReadToEnd()方法则需要1000 MS。

这就是事情 - 调用Response.GetResponseStream()的能力并不意味着你得到了回应&#34;。你得到的只是确认响应在那里。

在现实世界中,这类似于收到您必须在邮局签名的包裹。邮局会在你的邮箱里放一张明信片,说邮局里有送货等候你。这是你的Response.GetResponseStream()电话。但此时你没有包裹,只有明信片上写着包裹在那里。现在你需要去邮局,向他们展示卡片,并检索包裹。这是StreamReader.ReadToEnd()来电。

时间几乎翻倍,因为大部分1000毫秒用于与远程服务器通信。如果你需要整个响应,你几乎无法做到加快速度。好消息是,由于时间花费在I / O上,因此很有可能您可以并行化此代码以从多个网站检索数据(假设您没有将网络加载到容量中)。 / p>

答案 2 :(得分:1)

ReadToEnd方法不是很慢,而是在等待需要时间的数据。

ReadToEnd方法足够快。我刚测试使用流读取器从内存流中读取一兆字节的数据,只需3毫秒。

当您从请求中获得响应流时,它只是开始获取请求的数据。一旦您已经读取了已经收到的数据,它就必须等待其余的数据到达。这是ReadToEnd电话中的时间。使用任何其他方式阅读流不会使它更快。