TCP接收窗口

时间:2012-04-23 23:18:10

标签: performance networking tcp latency throughput

我试图了解接收器窗口如何影响高延迟连接的吞吐量。

我在两台相距很远的机器上有一对简单的客户端 - 服务器应用程序,两个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毫秒......

4 个答案:

答案 0 :(得分:7)

我可以从你提供的样本中猜出两件事:

  1. 服务器的发送缓冲区大约为15k。
  2. 您提供的转储是在服务器端完成的。
  3. 对于要扩展到特定大小的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=14601Ack=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毫秒暂停似乎是暂停。

Slow-Start

  

算法最初在指数增长阶段开始,拥塞窗口大小(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循环