在进行网络I / O时是否缓冲了Stream.Read?

时间:2012-01-19 22:32:11

标签: c# stream io network-protocols buffered

所以我最近做了一些工作,当有人告诉我,如果在网络流上做Stream.Read是通过在GetResponseStream上调用.NET WebResponse之一获得的,或者那些是缓冲的。

他说如果你要在你正在阅读的代码中设置一个断点,你就不会停止网络流量。我发现这很奇怪,但也希望这是真的。这是如何运作的?它甚至是准确的吗?

using (Stream webResponseStream = this.webResponse.GetResponseStream())
{
   byte[] readBuffer = new byte[bufferSize];
   int bytesRead = webResponseStream.Read(readBuffer, 0, bufferSize);
   while (bytesRead > 0)
   {
        bytesRead = webResponseStream.Read(readBuffer, 0, bufferSize);
        // If I put a breakpoint here, does network activity stop?
   }
}

3 个答案:

答案 0 :(得分:5)

不, GetResponseStream 返回的Stream对象未缓冲。

对你的第二部分(关于设置断点)的简短回答是你的同事不正确。网络流量将停止,但最终会形成“最终”,请继续阅读以获取更多详细信息。

Bing 表示“SO_RCVBUF”,“tcp接收窗口大小”,“vista自动缩放”,以获取更一般的信息。

详细部分

让我们从这开始,这是Windows网络堆栈的文本视图:

++ .NET Network API's

++ --- Winsock DLL(用户模式)

++ ------ afd.sys(内核模式)

++ --------- tcpip.sys

++ ------------ ndis

++ --------------- network interface(hal)

这是一个粗略的堆栈,掩盖了一些细节,但一般的想法是.NET调用Winsock用户模式dll,然后将大部分实际工作推送到它的堂兄 AFD (辅助功能驱动程序),继续到tcpip子系统,等等..

AFD 级别,有一个缓冲区,通常介于 8K和64K 之间,但是对于Vista(及更高版本),它也可以向上扩展。此设置也可以通过注册表设置( HKLM \ SYSTEM \ CurrentControlSet \ services \ AFD \ Parameters )进行控制。

此外,tcpip.sys还有一个缓冲区,类似于AFD的缓冲区。我相信打开套接字时传递的* SO_RCVBUF *设置也可以改变它。

基本上,当您接收数据时,代表您的 tcpip.sys 会不断获取数据,并告知发件人它已获取数据( ACK 的),并且直到其缓冲区已满。但与此同时, afd.sys 正在通过询问数据(然后将其复制到自己的缓冲区中)来清除 tcpip.sys 缓冲区,因此 tcpip.sys 可以从发件人那里填充更多数据。

然后是你(.NET API调用者),他也在做同样的事情,调用Read()方法并将数据复制到缓冲区。

所以,如果你考虑一下,一条256Kb的消息通过网络传输,64K位于 tcpip.sys 缓冲区,64K位于 afd.sys 缓冲区,并且在请求一个4K(你的bufferSize变量)块之后你设置一个断点,我们看到收到的128K ACK'回发送器,并且因为 tcpip.sys 缓冲区已满(现在假设64K大小)(并且你被调试会话阻止了), tcpip.sys 将没有选项,只能告诉发送者停止通过网络发送字节数,因为它不能快速处理它们。

实际上(即有人没有设置断点!),我看到GC会引发这种行为。看到一个3秒垃圾收集的案例,让所有操作系统缓冲区都填满。

答案 1 :(得分:2)

这是准确的。 TCP由Windows TCP / IP驱动程序堆栈实现。在程序中设置断点不会阻止驱动程序从服务器下载数据。直到驱动程序决定使用太多内核池空间来缓冲数据。具体规则未记录。

这是一种优化,是操作系统中的标准优化。该策略使TCP传输非常有效,而且程序的响应速度并没有调整,只能通过连接的带宽以及驱动程序堆栈对网卡中断的响应速度。它非常擅长,这是司机的工作。

答案 2 :(得分:0)

默认情况下,NetworkStream未缓存。当您在正在读取此流的过程中放置​​断点时,正在向底层套接字发送数据的客户端将阻塞并等待,直到远程套接字再次准备好接收为止。客户端将无法写入套接字,因此,是的,网络流量停止

这是一个blog post,它说明了如何使用BufferedStream类来缓冲它。