实际负责下载数据的GetResponseStream()或ReadBytes()以及如何?

时间:2014-01-22 10:51:13

标签: c# .net system.net.httpwebrequest

如果我们创建一个HttpWebRequest并从其响应中获取ResponseStream,那么数据是否会立即完全下载,或者当我们调用流的ReadBytes时,只有数据将从网络下载然后读取内容?

我想参考的代码示例如下:

var webRequest = HttpWebRequest.Create('url of a big file approx 700MB') as HttpWebRequest;
var webResponse = webRequest.GetResponse();
using (BinaryReader ns = new BinaryReader(webResponse.GetResponseStream()))
{
   Thread.Sleep(60000); //Sleep for 60seconds, hope 700MB file get downloaded in 60 seconds
   //At this point whether the response is totally downloaded or will not get downloaded at all
   var buffer = ns.ReadBytes(bufferToRead);
   //Or, in the above statement ReadBytes function is responsible for downloading the content from the internet.
}

3 个答案:

答案 0 :(得分:4)

GetResponseStream会打开并返回Stream个对象。流对象来自底层Socket。此Socket由网络适配器异步发送数据。数据刚到达并被缓冲。 GetResponseStream将阻止执行,直到第一个数据到达

ReadByte将数据从套接字层提取到c#。 此方法将阻止执行,直到有一个字节可用

过早关闭流将结束异步传输(关闭Socket发件人将收到通知,因为他们的连接将失败)并丢弃(刷新)任何缓冲的数据你还没有用过。

答案 1 :(得分:3)

var webRequest = HttpWebRequest.Create('url of a big file approx 700MB') as HttpWebRequest;

好的,我们准备好了。如果您对自己的流进行PUT或POST,情况会有所不同,但差异是类似的。

var webResponse = webRequest.GetResponse();

GetResponse()返回时,它至少会读取所有HTTP标头。它可能已经读取了重定向的标头,并对重定向到的URI执行了另一个请求。它也可能实际上是在点击缓存(直接或因为webserver setnt 304 Not Modified),但默认情况下会隐藏你的详细信息。

套接字缓冲区中可能会有更多字节。

using (BinaryReader ns = new BinaryReader(webResponse.GetResponseStream()))
{

此时,我们已经有了代表网络流的流。

让我们删除Thread.Sleep()它除了增加连接超时风险外什么都不做。即使假设它在等待时没有超时,连接也会退出"从发送字节起,因为你没有阅读它们,所以效果将比你通过添加故意放慢速度更慢。

var buffer = ns.ReadBytes(bufferToRead);

此时,已读取bufferToRead字节以创建byte[]或小于bufferToRead,因为流的总大小小于buffer,在这种情况下{ {1}}包含整个流。这需要花费的时间。

}

此时,由于执行了成功的HTTP GET,底层的Web访问层可以缓存响应(如果它非常大,可能不会 - 默认情况是非常大量请求不会重复,也不会从缓存中获益。

错误条件会在异常发生时引发异常,并且在这种情况下不会进行任何缓存(没有缓存错误响应的点)。

没有必要睡觉,或者等待"等待"在它上面。

值得考虑以下变体,它通过直接操作流而不是通过读取器来处理稍微低一级的变量:

using(var stm = webResponse.GetResponseStream())
{

我们将直接在该流上工作;

byte[] buffer = new byte[4096];
do
{
    int read = stm.Read(buffer, 0, 4096);

这将返回最多 4096字节。它可能读得少,因为它有一块已经可用的字节,并且它会立即返回多个字节。它只返回0字节,如果它在流的末尾,所以这给了我们在等待和不等待之间的平衡 - 它承诺等待足够长的时间来获得至少一个字节,但它是否等到它得到所有4096个字节都取决于流,以选择等待那么长或返回更少的字节是否更有效;

    DoSomething(buffer, 0, read);

我们使用我们得到的字节。

} while(read != 0);

Read()只给出了零字节,如果它在流的末尾。

}

同样,当流处理时,响应可能会缓存,也可能不会缓存。

正如您所看到的,即使在最低级别.NET使我们能够在使用HttpWebResponse时访问,也无需添加代码来等待任何事情,因为这总是为我们完成。

您可以使用对流的异步访问来避免等待,但异步机制仍然意味着您可以获得结果。

答案 2 :(得分:1)

要回答有关流式传输开始的问题,GetResponseStream()将开始从服务器接收数据。但是,在某些时候,如果您没有读取缓冲区,网络缓冲区将变满,服务器将停止发送数据。有关tcp缓冲区等的详细说明,请参阅here

因此,60000的睡眠对您没有多大帮助,因为沿途的网络缓冲区将会填满,数据将停止到达,直到您将其读取为止。最好把它读掉,然后在你去的时候把它写成块。

有关ResponseStream here工作原理的更多信息。 如果您想知道要使用的缓冲区大小,请参阅here