所以我最近做了一些工作,当有人告诉我,如果在网络流上做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?
}
}
答案 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类来缓冲它。