在进行网络I/O时,Stream.Read是否具有缓冲功能?

3

最近我在进行一些工作时,有人告诉我,在从.NET的WebResponse调用GetResponseStream,或者那些被缓冲的网络流上执行Stream.Read操作时,你不会停止网络流量。我觉得这很奇怪,但也希望它是真的。那么它是如何工作的?这个说法准确吗?

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?
   }
}

什么类型的网络流?它来自哪里? - John Saunders
TCP,我只需打开一个 WebRequest,进行 POST 到 xyz.com,然后执行 EndGetResponse(),接着获取 webResponse 的 GetResponseStream,并在其上调用 .Read() 直到没有更多数据。 - halivingston
1
如果您发布具体的代码示例,那么可读性会更好。 - MethodMan
那不是“TCP”,那是HTTP。 - John Saunders
1
是的,更准确地说是基于TCP的HTTP。 - halivingston
不确定为什么有人给这个问题点了“踩”,这是一个好问题。 - Sasha Chedygov
3个回答

5
不,由GetResponseStream返回的Stream对象没有缓冲。
关于你同事关于设置断点的第二部分的简短回答是错误的。网络流量会停止,但最终会恢复,具体“最终”是什么意思,请继续阅读了解更多细节。
可以通过Bing搜索“SO_RCVBUF”、“tcp接收窗口大小”、“vista自动缩放”等来获取更多的一般信息。
详细部分:
让我们从这里开始,这是Windows网络堆栈的文本视图:
++ .NET网络API
++ --- Winsock DLL(用户模式)
++ ------ afd.sys(内核模式)
++ --------- tcpip.sys
++ ------------ ndis
++ --------------- 网络接口(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变量)的chunk之后设置了一个断点,我们会看到128K的ACK被接收者返回给发送者,并且由于tcpip.sys缓冲区已经满了(假设大小为64K),现在你受到调试会话的阻塞,tcpip.sys将不得不告诉发送者停止发送字节,因为它无法快速处理它们。
实际上(即没有设置断点的情况下!),我见过GC引起这样的行为。曾经看到一个3秒钟的垃圾回收让所有的操作系统缓冲区都填满了。

2
这是准确的。TCP由Windows TCP/IP驱动程序堆栈实现。在程序中设置断点并不会阻止驱动程序从服务器下载数据。直到驱动程序决定使用过多的内核池空间来缓冲数据。其确切规则未经记录。
这是一种优化,在操作系统中是标准优化之一。该策略使TCP传输非常高效,它不受您的程序响应速度的限制,仅受连接带宽和驱动程序对网络卡中断的响应速度的影响。驱动程序擅长这个,这是驱动程序的工作。

NetworkStream对象本身是没有缓冲区的。正如您所说,这是在操作系统级别进行的。 - Sasha Chedygov

0

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

这里有一篇博客文章,说明了如何使用BufferedStream类使其具有缓冲功能。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接