1

如果我们创建一个 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.
}
4

3 回答 3

4

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

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

过早关闭流将结束异步传输(关闭Socket发送方将收到通知,因为他们的连接将失败)并丢弃(刷新)您尚未使用的任何缓冲数据。

于 2014-01-22T11:01:57.050 回答
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已读取字节以创建 a ,要么byte[]小于,bufferToRead因为流的总大小小于该字节,在这种情况下buffer包含整个流。这将需要尽可能长的时间。

}

此时,由于执行了成功的 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 中,我们也可以在使用 .NET 时访问HttpWebResponse,无需添加代码来等待任何内容,因为我们总是这样做。

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

于 2014-01-22T11:34:47.993 回答
1

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

因此,您的 60000 睡眠不会对您有太大帮助,因为沿途的网络缓冲区将填满,并且数据将停止到达,直到您将其读取为止。最好把它读下来,然后把它分块写下来。

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

于 2014-01-22T11:12:00.203 回答