我试图了解接收器窗口如何影响高延迟连接的吞吐量。
我在两台相距很远的机器上有一对简单的客户端 - 服务器应用程序,两个250mSec延迟RTT之间的连接。我使用Windows(XP,7)和Linux(Ubuntu 10.x)运行此测试,结果相同,因此为简单起见,我们假设: 客户端接收数据:WinXP Pro 服务器发送数据:Win7 Pro 同样,延迟是250毫秒RTT。
我在不更改客户端上的接收器缓冲区大小的情况下运行TCP测试(默认为8Kb),我在线上看到(使用Wireshark):
查看跟踪,我看到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毫秒......
答案 0 :(得分:7)
我可以从你提供的样本中猜出两件事:
对于要扩展到特定大小的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=19305
之前服务器收到的最后一个ACK seq没有,并且服务器在该点发送的最后一个数据包的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标头小于正在使用的窗口。
答案 1 :(得分:1)
连接套接字后,无法将接收缓冲区大小设置为> = 64k。你必须先做。在服务器的情况下,这意味着在侦听套接字上设置接收缓冲区大小:接受的套接字从它们被接受的套接字继承它。如果不这样做,则无法协商TCP窗口缩放选项,因此对等方无法相互告知64k以上的大小。
答案 2 :(得分:0)
当发送机器从接收器接收到ack时,发送机器排队发送了多少数据?由于TCP是基于流的协议,在数据流中没有数据包中断,因此TCP发送方无法知道何时应该发送部分数据包,何时应该等待更多数据到达。通常,如果TCP实现收到数据包的ACK,它将决定在发送缓冲区为空之前发送任何内容,但是如果数据在此之后排队进行传输,它可能会等到它再发送另一个ACK之后再发送另一个ACK。批次。
答案 3 :(得分:0)
接收器窗口大小直接影响吞吐量。吞吐量< = RWIN / RTT。
一些事情也会降低吞吐量。标头中的ECN位是否设置为1?你知道两边是否有丢包?看起来服务器正在超时。可以在客户端打印传入数据包和传出ACK的序列号,并在服务器端打印类似信息。如果接收器的序列号是5并且它接收到6,7,8,9则它将确认6,7,8,9。但是如果6丢失,它将在收到数据包7,8,9时确认5。序列号可以显示很多信息。
250毫秒暂停似乎是暂停。
算法最初在指数增长阶段开始,拥塞窗口大小(cwnd)为1或2个段,并且对于每个接收到的ACK,将其增加1个段大小(SS)。由于接收器通常为每两个段发送ACK,因此该行为有效地使每次往返网络的窗口大小加倍。此行为一直持续到拥塞窗口大小(cwnd)达到接收方通告窗口的大小或发生丢失为止。
可能发生的事情是
服务器发送1个数据包,得到1个ack
服务器发送2个数据包,获得2个acks(2,3)
服务器发送4个数据包,得到4个acks(4,5,6,7)
服务器发送8个数据包,获得4个acks(在客户端获取之前丢弃数据包)(8,9,10,11)(超时12)
服务器发送4个包,得到4个acks(12,13,14,15)
服务器发送5个数据包,得到4个acks(16,17,18,19)(超时20)
服务器发送3个包,获得3个acks(20,21,22)
服务器发送4个包,得到4个acks(23,24,25,26)
服务器发送5个数据包,得到4个acks(27,28,29,30)(超时31)
服务器继续3,4,5循环