进行网络 I/O 时 Stream.Read 是否被缓冲?
因此,我最近正在做一些工作,当有人告诉我,如果在通过在 GetResponseStream
上调用 .NET 的 GetResponseStream
之一获得的网络流上执行 Stream.Read
, code>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?
}
}
So I was recently doing some work, when somebody told me that if doing a Stream.Read
on a network stream that is obtained from calling one of .NET's GetResponseStream
on a WebResponse
or those are buffered.
He was saying that if you were to put a breakpoint, in the code where you're reading, you wouldn't stop the network traffic. I find that bizzare, but also hoping that it's true. How does that work? Is it even accurate?
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?
}
}
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(3)
不,GetResponseStream 返回的 Stream 对象没有被缓冲。
对第二部分(关于设置断点)的简短回答是,您的同事是不正确的。网络流量将会停止,但最终会停止,并且要描述“最终”,请继续阅读以了解更多详细信息。
Bing“SO_RCVBUF”、“tcp 接收窗口大小”、“vista 自动缩放”,以获取更一般的信息。
详细部分
让我们从这里开始,这是 Windows 网络堆栈的文本视图:
++ .NET Network API's
++ --- Winsock DLL(用户模式)
++ ------ afd .sys(内核模式)
++ --------- tcpip.sys
++ ------------ ndis
++ ------------- -- 网络接口 (hal)
这是一个粗略的堆栈,掩盖了一些细节,但总体思想是 .NET调用 Winsock 用户模式 dll,然后将大部分实际工作推送到它的表兄弟 AFD(辅助功能驱动程序),然后再推送到 tcpip 子系统,依此类推
。 级别,有一个缓冲区,通常在8K和64K之间,但对于Vista(及更高版本),它也可以扩展。此设置也可以通过注册表设置(HKLM\SYSTEM\CurrentControlSet\services\AFD\Parameters)来控制。
另外,tcpip.sys也有一个缓冲区,与AFD的缓冲区类似。我相信打开套接字时传递的 *SO_RCVBUF* 设置也可以改变这一点。
本质上,当您接收数据时,tcpip.sys 代表您不断获取数据,并不断告诉发送者它已获取数据(ACK),并且这样做直到其缓冲区已满。但与此同时,afd.sys 通过向 tcpip.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 秒垃圾回收的情况,它让所有操作系统缓冲区都填满了。
No, the Stream object returned by GetResponseStream is not buffered.
Short answer to your second part (about setting a breakpoint) is that your co-worker is incorrect. Network traffic will stop, but eventually, and to describe "eventually", read on for more details.
Bing for "SO_RCVBUF", "tcp receive window size", "vista auto scaling", for even more general information.
Detailed Part
Let's start with this, here's a textual view of the Windows networking stack:
++ .NET Network API's
++ --- Winsock DLL (user mode)
++ ------ afd.sys (kernel mode)
++ --------- tcpip.sys
++ ------------ ndis
++ --------------- network interface (hal)
This is a rough stack, glossing over some details, but the general idea is that .NET calls into Winsock user-mode dll, which then pushes most of the real work to its cousin AFD (Ancillary Function Driver), onwards to the tcpip sub system, so on ..
At the AFD level, there is a buffer, generally between 8K and 64K, but with Vista (and beyond), it can also scale up. This setting can also be controlled by a registry setting(HKLM\SYSTEM\CurrentControlSet\services\AFD\Parameters).
In addition, the tcpip.sys also has a buffer, that is similar to AFD's buffer. I believe *SO_RCVBUF* setting passed when opening the socket can change this too.
Essentially, when you are receiving data, tcpip.sys on your behalf keeps getting data, and keeps the telling the sender that it got the data (ACK's), and does so until its buffers are full. But at the same time, afd.sys is clearing tcpip.sys buffers by asking it for the data (which it then copies into its own buffer), so tcpip.sys can fill more data from the sender.
And then there's you (the .NET API caller), who is also doing the same, calling the Read() method and copying data into your buffer.
So, if you think about it, a 256Kb message coming over the wire, 64K sitting in the tcpip.sys buffer, 64K sitting in afd.sys buffer, and you set a breakpoint after asking for one 4K (your bufferSize variable) chunk, we're looking at 128K ACK'ed back to the sender as received, and since the tcpip.sys buffer is full (assuming 64K size) now (and you're blocked by your debugging session), tcpip.sys will no have option but to tell the sender to stop sending being bytes over the wire, because it can't process them quick enough.
Practically (i.e. somebody not setting a breakpoint!), I've seen GC to induce such behavior. Seen a case of a 3 second garbage collection that let all the OS buffers fill up.
这是准确的。 TCP 由 Windows TCP/IP 驱动程序堆栈实现。在程序中设置断点不会阻止驱动程序从服务器下载数据。直到驱动程序决定使用过多的内核池空间来缓冲数据。确切的规则没有记录。
这是一种优化,操作系统中的标准优化。该策略使 TCP 传输非常高效,它不取决于程序的响应速度,而仅取决于连接的带宽以及驱动程序堆栈对网卡中断的响应速度。它非常擅长,这是司机的工作。
This is accurate. TCP is implemented by the Windows TCP/IP driver stack. Setting a breakpoint in your program does not stop the driver from downloading data from the server. Not until the driver decides that too much kernel pool space is being used to buffer the data. The exact rules for which are undocumented.
This is an optimization, a standard one in operating systems. The strategy makes TCP transfers very efficient, it isn't paced by how responsive your program is, only by the bandwidth of the connection and how responsive the driver stack is to network card interrupts. Which it is very good at, it is a driver's job.
默认情况下,NetworkStream 不进行缓冲。当您在读取此流的过程中放置断点时,向底层套接字发送数据的客户端将阻塞并等待,直到远程套接字准备好再次接收。客户端将无法写入套接字,因此,是的,网络流量停止。
这是 博客文章 说明了如何使用 BufferedStream 类。
A NetworkStream is not buffered by default. When you put a breakpoint inside the procedure that is reading this stream, the client that is sending data to the underlying socket will block and wait until the remote socket is ready to receive once again. The client won't be able to write to the socket, so, yes, the network traffic stops.
Here's a blog post which illustrates how you could make it buffered by using the BufferedStream class.