不幸的是,Jon Skeet 的回答遗漏了大部分内容 - 发送缓冲区大小,以及您正在写入的管道的 bandwidth-delay product。
如果您尝试使用单个套接字通过大管道发送数据,并且希望 TCP 填充该管道,则需要使用与管道的带宽延迟乘积相等的发送缓冲区大小。否则,TCP 将不会填充管道,因为它不会始终留下足够的“正在传输的字节”。
TCP 为您处理数据包丢失,这意味着它必须有缓冲区来保存您提供给它的数据,直到它可以确认数据已被另一方正确接收(通过 TCP ACK)。没有缓冲区是无限的,因此在某个地方必须有一个限制。该限制是任意的,您可以选择任何您想要的,但您需要确保它足够大以处理连接的 BDP。
考虑一个缓冲区大小正好为:1 字节的 TCP 套接字。您正在尝试通过比特率为 1 gbit/sec 且单向延迟为 1 ms 的连接发送数据。
- 您将第一个字节提供给 TCP 套接字。
- 套接字阻止任何进一步的写入调用(发送缓冲区已满)。
- TCP 发送一个字节。 gig eth 适配器的批量传输速率为每字节 8 ns,因此传输时间可以忽略不计。
- 1 毫秒后,接收方获得 1 个字节。
- 1 毫秒后,您会收到来自接收器的确认。
- TCP 会从发送缓冲区中删除第一个字节,因为它已确认接收方正确获得了该字节。
- 发送缓冲区解除阻塞,因为它有空间。
- 你给 TCP 套接字你的第二个字节...
- 等等。
此连接传输数据的速度有多快?发送 1 个字节需要 2 毫秒,因此,此连接的比特率为 500 字节/秒 == 4 kbit/秒。
哎呀。
假设连接速度为 1 Gb,单向延迟平均为 10 毫秒。往返时间(也就是从您的套接字发送数据包到它收到该数据包的 ack 并因此知道要发送更多数据的时间)通常是延迟的两倍。
因此,如果您有一个 1 Gb 的连接和 20 毫秒的 RTT,那么该管道有 1 Gb/秒 * 20 毫秒 == 2.5 兆字节的数据,如果它被完全利用的话。
如果您的 TCP 发送缓冲区小于 2.5 兆字节,那么一个套接字将永远无法充分利用管道 - 您永远无法从套接字中获得千兆位/秒的性能。
如果您的应用程序使用许多套接字,则所有 TCP 发送缓冲区的总大小必须为 2.5 MB,才能充分利用这个假设的 1 吉比特/20 毫秒 RTT 管道。例如,如果您使用 8192 字节的缓冲区,则需要 306 个并发 TCP 套接字来填充该管道。
编辑问题:
计算 BDP 只是将带宽乘以
往返延误,注意单位。
因此,如果您的连接速度为 1 Gb/秒,往返时间为 20 毫秒,那么发生的情况是您乘以位/秒 * 秒,因此秒数抵消,剩下的是位.转换为字节,您就有了缓冲区大小。
- 1 gbit/sec * 20 ms == 1 * gbit/sec * 0.02 sec == (1 * 0.02) gbit
- 0.020 gbit == 20 MBit。
- 20 Mbit * 1 字节 / 8 位 == 20 / 8 MBytes == 2.5 MBytes。
因此,我们的 TCP 缓冲区需要设置为 2.5 MB 以使这个组成的管道饱和。