【问题标题】:What are SO_SNDBUF and SO_RCVBUFSO_SNDBUF 和 SO_RCVBUF 是什么
【发布时间】:2011-05-14 12:46:39
【问题描述】:

您能解释一下SO_SNDBUFSO_RCVBUF 选项到底是什么吗?

好的,出于某种原因,操作系统会缓冲传出/传入的数据,但我想澄清一下这个主题。

  • 他们的作用是什么(通常)?
  • 它们是每个套接字的缓冲区吗?
  • 传输层的缓冲区(例如 TCP 缓冲区)与这些缓冲区之间是否存在连接?
  • 在使用流套接字 (TCP) 和使用无连接套接字 (UDP) 时,它们是否有不同的行为/角色?

一篇好文章也会很棒。

我搜索了它,但没有找到任何有用的信息。

【问题讨论】:

    标签: windows sockets network-programming


    【解决方案1】:

    他们的角色(通​​常)是什么?

    您想通过套接字发送的数据被复制到套接字的发送缓冲区,因此您的代码不必等待(=block)直到数据真正发送到网络。当 send 调用成功返回时,这仅意味着数据已被放入发送缓冲区中,一旦准备好通过网络发送该数据,协议实现就会从该缓冲区读取它。

    请记住,来自多个进程的多个套接字可能都希望同时发送数据,但任何时候都只能通过网络线路发送一个数据包。在发送过程中,所有其他发送者必须等待,一旦线路空闲,实现只能处理一个又一个发送请求。

    来自网络的数据由协议实现写入套接字的接收缓冲区,在那里它将等待您的代码从那里读取它。否则,所有接收都必须停止,直到您的代码处理完传入的数据包,但是您的代码可能会在数据包到达后台时执行其他操作,并且接口是共享的,因此系统必须避免其他进程无法接收其网络数据只是因为您的进程拒绝处理自己的传入数据。

    它们是每个套接字的缓冲区吗?

    是的。每个套接字都有自己的一组缓冲区。

    传输层的缓冲区(例如 TCP 缓冲区)和这些缓冲区之间是否存在连接?

    我不确定您所说的“TCP 缓冲区”是什么意思,但如果您指的是 TCP 接收和发送窗口,答案是肯定的。

    TCP 会定期告诉对方你的接收缓冲区还有多少空间,这样对方发送的数据永远不会超过你的接收缓冲区的容量。如果您的接收缓冲区已满,对方将完全停止发送,直到再次有空间为止,一旦您从中读取一些数据,就会出现这种情况。

    因此,如果您无法像防止套接字缓冲区满载所需的频率那样频繁地读取数据,则增加接收缓冲区大小可以防止 TCP 连接不得不暂停发送数据。

    另一方面,如果发送缓冲区已满,则套接字将不再从您的代码中接受任何数据。任何发送尝试都会阻塞或失败并出现错误(非阻塞套接字),直到有空间为止。

    由于 TCP 只能处理当前在发送缓冲区中的数据,发送缓冲区的大小也会影响 TCP 的发送行为。 TCP 发送策略可能取决于各种因素。其中之一是已知要发送的数据量。如果您的发送缓冲区只有 2 KB,那么 TCP 将永远不会看到超过 2 KB 的发送空间,即使您的应用程序可能知道接下来会有更多数据。如果您的发送缓冲区为 256 KB,并且您将 128 KB 的数据放入其中,TCP 将知道它必须为此连接发送 128 KB 的数据,这可能(并且很可能会)影响 TCP 使用的发送策略。

    在使用流套接字 (TCP) 和使用无连接套接字 (UDP) 时,它们的行为/角色是否不同?

    是的。那是因为对于 TCP,您发送的数据只是一个字节流。字节与发出的数据包之间没有关系。发送 80 字节可能意味着发送一个 80 字节的数据包或发送 10 个每个 8 字节的数据包。 TCP 将自行决定。传入也一样。如果您的接收缓冲区中有 200 个字节,您将无法知道它们是如何到达那里的,您从 TCP 套接字读取的字节数量可能已经使用任意数量的数据包进行传输。因此,尽管通过基于数据包的网络以块的形式传输数据,但 TCP 连接的行为类似于串行线路链路。

    另一方面,UDP 发送数据报。如果您将 80 个字节放入 UDP 套接字的发送缓冲区,那么这 80 个字节肯定会在包含 80 个字节有效负载数据的单个 UDP 数据包中发送出去。数据的发送方式与您将其写入发送缓冲区的方式完全相同。如果你一个接一个地写80个字节,就会发出80个包,每个包一个字节。如果您告诉 TCP 套接字发送 200 个字节,但发送缓冲区中只剩下 100 个字节空间,TCP 将向缓冲区添加 100 个字节,并让您知道 200 个字节中的 100 个已添加。另一方面,UDP 将阻塞或失败并出现错误,因为所有 200 个字节都适合或不适合; UDP 不适合。

    同样在接收时,数据报存储在 UDP 接收缓冲区中,而不是字节。如果 TCP 套接字首先接收 80 字节数据,然后接收 200 字节数据,您可以执行一次读取所有 280 字节的读取调用。如果 UDP 套接字首先接收到一个 80 字节的数据报,然后是一个 200 字节的数据报,并且您请求从中读取 280 个字节,那么您得到的正好是 80 个字节,因为读取调用返回的所有数据都必须来自同一个数据报。您不能跨数据报边界读取。另请注意,如果您请求仅读取 20 个字节,则会收到数据报的前 20 个字节,而其他 60 个字节将被丢弃。下次读取数据时,将从下一个大小为 200 字节的数据报中读取。

    所以两句话的区别:TCP 套接字将字节存储在套接字缓冲区中,UDP 套接字将数据报存储在套接字缓冲区中。并且数据报必须完全适合缓冲区,不能完全适合套接字缓冲区的传入数据报会被静默丢弃,即使缓冲区有一些可用空间。

    【讨论】:

    • 这是一个很好的答案。谢谢!
    • 优秀的回答人!万分感谢。这是一堂很棒的课。
    【解决方案2】:

    以上答案并没有回答所有问题,尤其是关于 Socket 缓冲区和 TCP 缓冲区之间的关系。

    我认为它们是不同层次的不同事物。 TCP缓冲区是Socket缓冲区的消费者。

    套接字缓冲区(输入和输出)是一个 IO 缓冲区,系统调用从用户空间中的应用程序代码访问该缓冲区。 例如,使用输出缓冲区,应用程序代码可以

    • 在缓冲区已满之前立即发送数据,并在缓冲区已满时被阻止。
    • 设置缓冲区大小。
    • 将缓冲区中的数据刷新到底层存储(TCP 发送缓冲区)。
    • 通过关闭流来关闭输出缓冲区。

    TCP 缓冲区(发送和接收)位于只有操作系统才能访问的内核空间中。 例如,使用 TCP 发送缓冲区,TCP 协议实现可以

    • 发送数据包并接受 ACK。
    • 保证数据包的交付和订购。
    • 通过调整飞行数据包窗口的大小来控制拥塞。

    顺便说一下,UDP 协议没有缓冲区,但 UDP 套接字仍然可以有 IO 缓冲区。

    这些是我的理解,我非常乐意得到任何反馈/修改/更正。

    【讨论】:

      【解决方案3】:

      在 Google 中搜索“SO_RECVBUF msdn”给了我...

      http://msdn.microsoft.com/en-us/library/ms740476(VS.85).aspx

      用选项表中的这些行回答你的“它们是每个套接字吗”:

      SO_RCVBUF int Specifies the total per-socket buffer space reserved for receives.
      SO_SNDBUF int Specifies the total per-socket buffer space reserved for sends.
      

      稍后会详细介绍:

      SO_RCVBUF 和 SO_SNDBUF

      当 Windows Sockets 实现支持 SO_RCVBUF 和 SO_SNDBUF 选项,一个应用程序可以请求不同的缓冲区大小 (更大或更小)。对 setsockopt 的调用可以成功,即使 执行没有提供请求的全部金额。一个 应用程序必须使用相同的选项调用 getsockopt 来检查 实际提供的缓冲区大小。

      【讨论】:

        【解决方案4】:

        “SO_”前缀用于“套接字选项”,所以是的,这些是每个套接字缓冲区的每个套接字设置。通常有系统范围的默认值和最大值。

        SO_RCVBUF 更容易理解:它是内核分配的缓冲区的大小,用于在数据通过网络到达给定套接字和被拥有此套接字的程序读取之间的时间里保存到达给定套接字的数据插座。使用 TCP,如果数据到达而您没有读取它,缓冲区将填满,发送方将被告知减速(使用 TCP 窗口调整机制)。对于 UDP,一旦缓冲区满了,新的数据包就会被丢弃。

        SO_SNDBUF,我认为,只对 TCP 重要(在 UDP 中,无论您发送什么都直接发送到网络)。对于 TCP,如果远程端没有读取,您可以填充缓冲区(以便远程缓冲区变满,然后 TCP 将此事实传达给您的内核,并且您的内核停止发送数据,而是将其累积在本地缓冲区中,直到它填上)。或者如果存在网络问题,它可能会填满,并且内核没有收到它发送的数据的确认。然后它将减慢在网络上发送数据的速度,直到最终传出缓冲区填满。如果是这样,应用程序将来对该套接字的 write() 调用将阻塞(或者如果您设置了 O_NONBLOCK 选项,则返回 EAGAIN)。

        这一切都在Unix Network Programming 书中得到了最好的描述。

        【讨论】:

        • UDP 似乎“直接进入网络”,但出于基本的安全原因、性能、流量控制等原因,所以 SO_SNDBUF 对 UDP 也很重要。见stackoverflow.com/questions/4165174/…
        • 在 UDP 中 SO_SNDBUF 可以确定最大数据报大小。它不会直接连接到网络。
        • SO_SNDBUF 确实很重要,即使对于 UDP 也是如此。如果发送缓冲区中有空间,那么您可以在短时间内以比 NIC 的线路速率更快的速度“发送”。一旦缓冲区已满,您的调用将减慢到 UDP 的 NIC 输出速率,因为它等待 NIC 上的空间打开。例如,这可以确保您的应用程序不会在 100 Mb/s NIC 上无缘无故地破坏自己。 O_NONBLOCK 也可能在这里起作用,我需要重新检查详细信息。
        【解决方案5】:

        在 Windows 中,发送缓冲区确实对 UDP 有影响。如果您将数据包发送出去的速度快于网络传输它们的速度,最终您将填满套接字输出缓冲区,并且 SendTo 将因“将阻塞”而失败。增加 SO_SNDBUF 将对此有所帮助。我必须同时增加发送和接收缓冲区以进行测试,以找到我可以在 Windows 机器和 Linux 机器之间发送的最大数据包速率。我还可以通过检测“将阻止”错误代码、休眠一点并重试来处理发送大小。但是增加发送缓冲区大小更简单。 Windows 中的默认值是 8K,在这个拥有 GB 内存的 PC 时代,这似乎不必要地小了!

        【讨论】:

        • 大缓冲区会在负载下引入延迟(滞后),研究“bufferbloat”。有多少可用内存无关紧要。
        猜你喜欢
        • 2015-04-25
        • 2021-07-10
        • 2013-04-15
        • 2012-04-21
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多