我在两台相距很远的机器上有一对简单的客户端 – 服务器应用程序,两个250mSec延迟RTT之间的连接.我使用Windows(XP,7)和Linux(Ubuntu 10.x)运行此测试,结果相同,因此为简单起见,我们假设:
客户端接收数据:WinXP Pro
服务器发送数据:Win7 Pro
同样,延迟是250毫安RTT.
我在不更改客户端上的接收器缓冲区大小的情况下运行TCP测试(默认为8Kb),我在线上看到(使用Wireshark):
>客户端向服务器发送ACKS,TCP数据包包含RWIN = 65k
>服务器发送数据并报告RWIN = 65k
查看跟踪,我看到3-4个数据包(有效载荷为1460字节)的突发,紧接着是从客户端机器发送到服务器的ACK,然后没有任何约250毫秒然后来自服务器的新数据包突发给客户.
因此,总而言之,服务器似乎甚至在填满接收者窗口之前就不会发送新数据.
为了做更多的测试,这次我还运行了相同的测试,在客户端机器上更改接收器的缓冲区大小(在Windows上,更改接收器的缓冲区大小最终会影响机器通告的RWIN).我希望在阻止ACK之前看到大量的数据包突发…并且至少有更高的吞吐量.
在这种情况下,我将recv缓冲区大小设置为100,000,000.从客户端到服务器的数据包现在有一个RWIN = 99,999,744(好吧,那很好),但不幸的是从服务器发送到客户端的数据模式仍然是相同的:短暂的突发,然后是漫长的等待.
为了确认我在线上看到的内容,我还测量了从服务器向客户端发送大量数据的时间.我没有看到使用大型RWIN或使用默认值的任何更改.
任何人都可以帮助我理解为什么更改RWIN并不会真正影响吞吐量?
几点说明:
– 服务器使用8Kb块的write()尽可能快地发送数据
– 正如我之前所说,我也看到了使用Linux的类似效果.更改接收器缓冲区大小会影响节点使用的RWIN,但吞吐量保持不变.
– 我分析了数百个数据包之后的跟踪,给TCP足够的时间启动机制放大CWIN大小.
正如所建议的那样,我在这里添加了一条线迹的小快照
No. Time Source Destination Protocol Length Info 21 2.005080 CCC.CCC.CCC.CCC sss.sss.sss.sss TCP 60 57353 > 21500 [ACK] Seq=1 Ack=11681 Win=99999744 Len=0 22 2.005109 sss.sss.sss.sss CCC.CCC.CCC.CCC TCP 1514 21500 > 57353 [ACK] Seq=19305 Ack=1 Win=65536 Len=1460 23 2.005116 sss.sss.sss.sss CCC.CCC.CCC.CCC TCP 1514 21500 > 57353 [ACK] Seq=20765 Ack=1 Win=65536 Len=1460 24 2.005121 sss.sss.sss.sss CCC.CCC.CCC.CCC TCP 1514 21500 > 57353 [ACK] Seq=22225 Ack=1 Win=65536 Len=1460 25 2.005128 sss.sss.sss.sss CCC.CCC.CCC.CCC TCP 946 21500 > 57353 [PSH, ACK] Seq=23685 Ack=1 Win=65536 Len=892 26 2.005154 CCC.CCC.CCC.CCC sss.sss.sss.sss TCP 60 57353 > 21500 [ACK] Seq=1 Ack=14601 Win=99999744 Len=0 27 2.007106 CCC.CCC.CCC.CCC sss.sss.sss.sss TCP 60 57353 > 21500 [ACK] Seq=1 Ack=16385 Win=99999744 Len=0 28 2.007398 sss.sss.sss.sss CCC.CCC.CCC.CCC TCP 1514 21500 > 57353 [ACK] Seq=24577 Ack=1 Win=65536 Len=1460 29 2.007401 sss.sss.sss.sss CCC.CCC.CCC.CCC TCP 1514 21500 > 57353 [ACK] Seq=26037 Ack=1 Win=65536 Len=1460 30 2.007403 sss.sss.sss.sss CCC.CCC.CCC.CCC TCP 1514 21500 > 57353 [ACK] Seq=27497 Ack=1 Win=65536 Len=1460 31 2.007404 sss.sss.sss.sss CCC.CCC.CCC.CCC TCP 1514 21500 > 57353 [ACK] Seq=28957 Ack=1 Win=65536 Len=1460 32 2.007406 sss.sss.sss.sss CCC.CCC.CCC.CCC TCP 1514 21500 > 57353 [ACK] Seq=30417 Ack=1 Win=65536 Len=1460 33 2.007408 sss.sss.sss.sss CCC.CCC.CCC.CCC TCP 946 21500 > 57353 [PSH, ACK] Seq=31877 Ack=1 Win=65536 Len=892 34 2.007883 CCC.CCC.CCC.CCC sss.sss.sss.sss TCP 60 57353 > 21500 [ACK] Seq=1 Ack=19305 Win=99999744 Len=0 35 2.257143 CCC.CCC.CCC.CCC sss.sss.sss.sss TCP 60 57353 > 21500 [ACK] Seq=1 Ack=22225 Win=99999744 Len=0 36 2.257160 CCC.CCC.CCC.CCC sss.sss.sss.sss TCP 60 57353 > 21500 [ACK] Seq=1 Ack=24577 Win=99999744 Len=0 37 2.257358 sss.sss.sss.sss CCC.CCC.CCC.CCC TCP 1514 21500 > 57353 [ACK] Seq=32769 Ack=1 Win=65536 Len=1460 38 2.257362 sss.sss.sss.sss CCC.CCC.CCC.CCC TCP 1514 21500 > 57353 [ACK] Seq=34229 Ack=1 Win=65536 Len=1460 39 2.257364 sss.sss.sss.sss CCC.CCC.CCC.CCC TCP 1514 21500 > 57353 [ACK] Seq=35689 Ack=1 Win=65536 Len=1460 40 2.257365 sss.sss.sss.sss CCC.CCC.CCC.CCC TCP 1514 21500 > 57353 [ACK] Seq=37149 Ack=1 Win=65536 Len=1460
如您所见,服务器停止在数据包#33发送数据.
客户端在旧数据包的数据包#34发送ACK(seq = 19305,在数据包#20上发送,此处未显示).
RWIN为100Mb,我希望服务器不会阻塞一段时间.
在20-30个数据包之后,服务器端的拥塞窗口应该足够大,以便发送比我看到的更多的数据包…
我假设拥塞窗口最终会增长到RWIN ……但是,即使在数百个数据包之后,模式仍然相同:数据数据然后阻塞250毫秒……
>服务器的发送缓冲区大约为15k.
>您提供的转储是在服务器端完成的.
对于要扩展到特定大小的TCP连接窗口,发送方的发送缓冲区和接收方的接收缓冲区都必须足够大.
使用的实际窗口是接收器提供/请求的接收窗口的最小值和发送器的OS设置发送缓冲区大小.
简而言之,您需要在服务器上配置发送缓冲区大小.
为了清理,让我们按包分析您的样本包.
服务器发送另一堆数据:
22 2.005109 sss.sss.sss.sss CCC.CCC.CCC.CCC TCP 1514 21500 > 57353 [ACK] Seq=19305 Ack=1 Win=65536 Len=1460 23 2.005116 sss.sss.sss.sss CCC.CCC.CCC.CCC TCP 1514 21500 > 57353 [ACK] Seq=20765 Ack=1 Win=65536 Len=1460 24 2.005121 sss.sss.sss.sss CCC.CCC.CCC.CCC TCP 1514 21500 > 57353 [ACK] Seq=22225 Ack=1 Win=65536 Len=1460 25 2.005128 sss.sss.sss.sss CCC.CCC.CCC.CCC TCP 946 21500 > 57353 [PSH, ACK] Seq=23685 Ack=1 Win=65536 Len=892
请注意PSH.这是一个标志,指示之间的任何跳跃已发送完整的数据块,请将其发送到另一端.
(在这种情况下,“完整”块是你的8kb)
当服务器仍在发送时,它会收到2个ACKS:
26 2.005154 CCC.CCC.CCC.CCC sss.sss.sss.sss TCP 60 57353 > 21500 [ACK] Seq=1 Ack=14601 Win=99999744 Len=0 27 2.007106 CCC.CCC.CCC.CCC sss.sss.sss.sss TCP 60 57353 > 21500 [ACK] Seq=1 Ack=16385 Win=99999744 Len=0
特别注意数字:Ack = 14601和Ack = 16385.这些数字是接收器确认的数据包的序列号.
Ack = 14601表示“我收到的所有内容都是seq no 14601”.
另请注意,这些是较旧的数据,而不是您提供的示例.
所以服务器处理这些ACK并继续发送数据:
28 2.007398 sss.sss.sss.sss CCC.CCC.CCC.CCC TCP 1514 21500 > 57353 [ACK] Seq=24577 Ack=1 Win=65536 Len=1460 29 2.007401 sss.sss.sss.sss CCC.CCC.CCC.CCC TCP 1514 21500 > 57353 [ACK] Seq=26037 Ack=1 Win=65536 Len=1460 30 2.007403 sss.sss.sss.sss CCC.CCC.CCC.CCC TCP 1514 21500 > 57353 [ACK] Seq=27497 Ack=1 Win=65536 Len=1460 31 2.007404 sss.sss.sss.sss CCC.CCC.CCC.CCC TCP 1514 21500 > 57353 [ACK] Seq=28957 Ack=1 Win=65536 Len=1460 32 2.007406 sss.sss.sss.sss CCC.CCC.CCC.CCC TCP 1514 21500 > 57353 [ACK] Seq=30417 Ack=1 Win=65536 Len=1460 33 2.007408 sss.sss.sss.sss CCC.CCC.CCC.CCC TCP 946 21500 > 57353 [PSH, ACK] Seq=31877 Ack=1 Win=65536 Len=892
这里我们有一个完整的数据块:1460 * 5 892 == 8192.
然后,在发送最后一个数据包后0.443毫秒,它又得到一个ACK:
34 2.007883 CCC.CCC.CCC.CCC sss.sss.sss.sss TCP 60 57353 > 21500 [ACK] Seq=1 Ack=19305 Win=99999744 Len=0
然后有一个几乎正好250毫秒的延迟,在此期间服务器什么都不发送,然后收到这些:
35 2.257143 CCC.CCC.CCC.CCC sss.sss.sss.sss TCP 60 57353 > 21500 [ACK] Seq=1 Ack=22225 Win=99999744 Len=0 36 2.257160 CCC.CCC.CCC.CCC sss.sss.sss.sss TCP 60 57353 > 21500 [ACK] Seq=1 Ack=24577 Win=99999744 Len=0
然后继续发送:
37 2.257358 sss.sss.sss.sss CCC.CCC.CCC.CCC TCP 1514 21500 > 57353 [ACK] Seq=32769 Ack=1 Win=65536 Len=1460 38 2.257362 sss.sss.sss.sss CCC.CCC.CCC.CCC TCP 1514 21500 > 57353 [ACK] Seq=34229 Ack=1 Win=65536 Len=1460 39 2.257364 sss.sss.sss.sss CCC.CCC.CCC.CCC TCP 1514 21500 > 57353 [ACK] Seq=35689 Ack=1 Win=65536 Len=1460 40 2.257365 sss.sss.sss.sss CCC.CCC.CCC.CCC TCP 1514 21500 > 57353 [ACK] Seq=37149 Ack=1 Win=65536 Len=1460
这里有两个非常有趣的事情需要注意.
首先,服务器在不等待ACK的情况下发送了多少字节.
在该延迟之前服务器收到的最后一个ACK seq没有Ack = 19305,并且服务器在该点发送的最后一个数据包的seq no是Seq = 30417.
在那个暂停期间,服务器发送的11112个字节尚未被客户端确认.
其次,这是服务器在发送一堆数据后立即收到的一个ACK,它没有触发它发送更多数据.这就好像ACK不够好.
之前收到的ACK是Ack = 16385,给出30417-16385 = 14032字节,由服务器在该点未确认发送.只有在收到seq no 24577的ACK后,将该计数减少到30417-24577 = 5840,服务器才会再次开始发送.
因此,与16k的有效窗口大小相比,8k的缓冲区大小的事实意味着吞吐量实际上有所降低,因为服务器将不会发送任何8k块,直到有足够的空间.
最后,对于那些想知道的人,有一个称为窗口缩放的TCP选项,它允许连接的一端声明窗口大小实际上是TCP头中数字的倍数.请参阅RFC 1323.该选项在SYN数据包中传递,因此它们在连接中不可见 – 仅提示窗口缩放有效,因为窗口大小TCP标头小于正在使用的窗口.