包大小、窗口大小和套接字缓冲区在TCP中的作用

5
在学习了“窗口大小”概念后,我理解的是它在发送数据包之前保留数据包,直到最早的数据包得到确认。一旦窗口被填满,随后的数据包将会被丢弃。我还读到过TCP是一种流协议,数据包与网络层的IP协议相关联。
我之前的假设是,在代码内声明一个缓冲区,把数据填充进去,然后使用socket发送这个缓冲区。我声明了一个10000字节的缓冲区,并通过10 Gbps链路重复使用socket发送它。
以下是我的假设和问题,请核实并帮忙:
  1. 如果我想发送64、256、512等字节的数据包,就需要在代码中声明相应大小的缓冲区,并使用send()命令将其发送出去。每次执行send()命令都会发送一个相应大小的数据包。
  2. 那么,如果我想研究数据包大小变化对吞吐量的影响,我需要做什么呢?需要在代码中改变缓冲区的大小吗?
  3. 我们使用SO_SNDBUF和SO_RECVBUF设置的socket缓冲区是什么?谷歌说它是socket的缓冲区空间。它与TCP窗口大小是否相同或不同?哪个参数更适合改变或增加吞吐量?
此外,socket缓冲区有三个参数:最小值、默认值和最大值。哪一个参数应该在我的实验中进行变化,以获得更相关的结果呢?
3个回答

4
如果我想发送64、256、512等字节的数据包,我需要在代码中声明相应大小的缓冲区,并通过套接字发送。每次执行send()命令将发送一个这么大的数据包。只有在禁用Nagle算法且大小小于路径MTU时才能这样做。不要依赖此方法。
如果我想研究数据包大小对吞吐量的影响,我该怎么做?不需要在代码中改变缓冲区大小。在接收端变化SO_RCVBUF即可。这是吞吐量的最大决定因素。
我们使用SO_SNDBUF和SO_RCVBUF设置套接字缓冲区。前者用于发送方的缓冲区大小,后者用于接收方的缓冲区大小。它们都在内核中。
这与TCP窗口大小相同。请参考上文。
哪个参数更适合变化以提高吞吐量?请参考上文。
此外,套接字缓冲区有三个参数:最小值、默认值和最大值。我应该变化哪一个来使实验更有意义?不需要变化它们。这些是系统级参数。只需在您的应用程序中特定的套接字上玩耍SO_SNDBUF和SO_RCVBUF即可。

缓冲区大小不应该自动调整吗?我从未见过有人调整缓冲区大小并获得更好的结果。 - usr
此外,这些缓冲区大小选项是否真的控制 TCP 窗口的任何部分?它们似乎是内核缓冲区。TCP 窗口是动态的,因此静态缓冲区大小无法对应于 TCP 窗口大小。 - usr
@usr SO_RCVBUF 确定了最大接收窗口。我已经说过了。它不是自动调整的。我进行了一个包含1700个数据点的大型实验,清楚地表明接收缓冲区大小是确定吞吐量最重要的因素。 - user207421

3
TCP协议是一个流协议,因此它不直接提供一种控制发送数据包的方式。但是,您可以通过禁用Nagle算法来使TCP堆栈发送数据包。这样,您send的所有数据都将立即发送而不是被缓冲。数据将被分成MTU大小的数据包,大约为1400字节。这取决于链接。
回答(2):禁用Nagling并使用小于1400字节的缓冲区调用send。使用Wireshark确保您得到了想要的结果。
缓冲区设置与此无关。我不知道有任何有效的理由去改变它们。
总的来说,这个问题可能是无意义的,因为您似乎想发送大量的数据。只需保持启用Nagling并send大缓冲区(例如64KB)。

这意味着我们不能发送超过MTU大小的数据包吗? - Vijnana Yogi
你如何发送一个超过最大数据包大小的数据包?这显然是不可能的。所以我不知道你的问题的目的是什么。 - usr
@CarloWood,您能解释一下这对网络造成了什么问题吗?禁用Nagling后,流量控制仍然有效。 - usr
@usr 谢谢您的回复...我想我弄错了 :/。这只是我从很久以前记忆中的一些东西,当您禁用 Nagle 算法时,即使路由器缓冲区已满,您仍会以任何速率广播。我刚刚阅读了它的实际作用(https://searchnetworking.techtarget.com/definition/Nagles-algorithm),现在可以看到,如果您的应用程序需要对(否则)小数据包进行高响应性,则关闭它可能是可取的(因此,我删除了我的评论)。 - Carlo Wood
@CarloWood 我明白了,没问题。您可以删除您的评论并标记我的评论以供审核人员进行清理。 - usr

2
我在Windows 10上进行了一些实验:
https://docs.python.org/3/library/socketserver.html#asynchronous-mixins获取的代码,
使用RawCap进行回环捕获,
使用WireShark观察结果。
主要客户端代码如下:
def client(ip, port, message):
    sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    sock.setsockopt(socket.SOL_SOCKET,socket.SO_RCVBUF, 100000)
    sock.connect((ip, port))
    sock.sendall(bytes(message, 'ascii'))
    response = str(sock.recv(1024), 'ascii')
    print("Received: {}".format(response))

这里是结果(服务器端口为11111):
Here is the result
您可以看到,TCP接收窗口大小与SO_RCVBUF相同,它可能是平台无关的,您可以在其他平台上验证它。
https://msdn.microsoft.com/en-us/library/windows/hardware/ff570832(v=vs.85).aspx 上:

SO_RCVBUF套接字选项确定底层传输使用的套接字接收缓冲区的大小。

已经验证过了。
此外,当我设置SO_SNDBUF = 100000时,它对客户端和服务器之间的TCP传输没有影响,因为如果客户端一次发送太多数据,服务器只能丢弃数据。
因此,如果您想将SO_RCVBUF更改为最大吞吐量,可以参考http://packetbomb.com/understanding-throughput-and-tcp-windows/,操作系统可能会提供用于检测理想发送积压(ISB)的功能。

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