【问题标题】:Is Stream.Read buffered when doing network I/O?Stream.Read 在进行网络 I/O 时是否缓冲?
【发布时间】:2023-04-05 19:39:01
【问题描述】:

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

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

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

【问题讨论】:

  • 什么样的网络流?它是从哪里来的?
  • TCP,我确实打开了一个 WebRequest,一个 POST 到 xyz.com,然后我执行 EndGetResponse(),然后我得到 webResponse 的 GetResponseStream,并在它上面调用 .Read(),直到它不再显示
  • 发布您的确切代码示例,它将提高可读性
  • 那是不是“TCP”,那是HTTP。
  • 是的 HTTP over TCP,更准确地说。

标签: c# stream io network-protocols buffered


【解决方案1】:

不,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's ),并一直这样做,直到其缓冲区已满。但与此同时,afd.sys 正在清除 tcpip.sys 缓冲区,向它询问数据(然后将其复制到自己的缓冲区中),所以 tcpip.sys 可以填充更多来自发送方的数据。

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

所以,如果你想一想,一条 256Kb 的消息通过网络传输,64K 位于 tcpip.sys 缓冲区中,64K 位于 afd.sys 缓冲区中,并且您在请求一个 4K(您的 bufferSize 变量)块后设置了一个断点,我们正在查看 128K ACK'ed 回发送者作为接收,并且由于 tcpip.sys 缓冲区已满(假设 64K 大小)现在(并且您被调试会话阻止),tcpip.sys 将别无选择,只能告诉发送者停止通过线路发送字节,因为它不能足够快地处理它们。

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

【讨论】:

    【解决方案2】:

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

    这是一种优化,是操作系统中的标准优化。该策略使 TCP 传输非常高效,它不受程序响应速度的影响,仅取决于连接的带宽以及驱动程序堆栈对网卡中断的响应速度。它非常擅长,这是司机的工作。

    【讨论】:

    • 然而,NetworkStream 对象本身并没有被缓冲。正如您所说,这在操作系统级别进行。
    【解决方案3】:

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

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

    【讨论】:

      猜你喜欢
      • 2015-07-05
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-01-14
      • 2010-10-16
      • 1970-01-01
      • 2016-06-04
      • 2015-02-13
      相关资源
      最近更新 更多