Nagle's Algorithm, ACK Delay 和 Rlogin echo



我也被建议在这里问,因为关于协议的特定问题是主题,但如果有人感兴趣,这个问题也有一个小奖励在ServerFault。

我正在阅读TCP数据流,延迟ACK内格尔算法

到目前为止,我明白了:

  1. TCP上的延迟ACK实现在收到的段的确认上创建延迟,以给应用程序在确认的同时写一些数据的机会,从而避免发送空的ACK数据包并导致网络拥塞。
  2. 纳格尔算法的实现说明你不能发送一个小的TCP段,而另一个小的段仍然没有被确认。这避免了流量被加载几个小图

在一些交互式应用程序中,例如Rlogin, Nagle's AlgorithmDelayed ACKs可能会"冲突":

Rlogin将键盘输入发送给服务器,一些键(如F1)产生超过一个字节(F1 = Escape +左括号+ M),这些字节可以在不同的段中发送,如果它们被一个接一个地传递给TCP。

服务器在拥有整个序列之前不会用回显进行应答,因此所有的ack将被延迟(期望来自应用程序的一些数据)。另一方面,客户端在发送下一个字节之前会等待第一个字节确认(尊重内格尔算法)。这种组合最终导致"延迟"Rlogin。

F1F2键在Rlogin上发送的tcpdump表示如下:
    type Fl key
1   0.0                 slip.1023 > vangogh. login: P 1:2(1) ack 2
2   0.250520 (0.2505)   vangogh.login > slip.1023: P 2:4(2) ack 2
3   0.251709 (0.0012)   slip.1023 > vangogh.login: P 2:4(2) ack 4
4   0.490344 (0.2386)   vangogh.login > slip.1023: P 4:6(2) ack 4
5   0.588694 (0.0984)   slip.1023 > vangogh.login: . ack 6
    type F2 key
6   2.836830 (2.2481)   slip.1023 > vangogh.login: P 4:5(1) ack 6
7   3.132388 (0.2956)   vangogh.login > slip.1023: P 6:8(2) ack 5
8   3.133573 (0.0012)   slip.1023 > vangogh.login: P 5:7(2) ack 8
9   3.370346 (0.2368)   vangogh.login > slip.1023: P 8:10(2) ack 7
10  3.388692 (0.0183)   slip.1023 > vangogh.login: . ack 10

现在的疑问是:即使我读到的页面声明服务器在拥有整个密钥序列之前没有回复,通过tcpdump捕获的数据包显示密钥在各自的ack上被回显(第一个回复是2字节长,因为来自ESC的回显是两个字符-插入符号+左括号)。

如果数据正在从应用程序发送到TCP(回声响应),为什么ack被延迟?根据所述,关于服务器在回滚它之前等待完整序列, ACK不应该包含任何回滚直到最后一个ACK,这将包含整个序列回滚?

编辑:

以下是修改后的Rlogin的tcpdump输出,没有Nagle算法(TCP_NODELAY标志):

        type Fl key
1   0.0     slip.1023 > vangogh.login: P 1:2(1) ack 2
2   0.002163 (0.0022)   slip.1023 > vangogh.login: P 2:3(1) ack 2
3   0.004218 (0.0021)   slip.1023 > vangogh.login: P 3:4(1) ack 2
4   0.280621 (0.2764)   vangogh.login > slip.1023: P 5:6(1) ack 4
5   0.281738 (0.0011)   slip.1023 > vangogh.login: . ack 2
6   2.477561 (2.1958)   vangogh.login > slip.1023: P 2:6(4) ack 4
7   2.478735 (0.0012)   slip.1023 > vangogh.login: . ack 6
        type F2 key
8   3.217023 (0.7383)   slip.1023 > vangogh.login: P 4:5(1) ack 6
9   3.219165 (0.0021)   slip.1023 > vangogh.login: P 5:6(1) ack 6
10  3.221688 (0.0025)   slip.1023 > vangogh.login: P 6:7(1) ack 6
11  3.460626 (0.2389)   vangogh.login > slip.1023: P 6:8(2) ack 5
12  3.489414 (0.0288)   vangogh.login > slip.1023: P 8:10(2) ack 1
13  3.640356 (0.1509)   slip.1023 > vangogh.login: . ack 10

可以注意到在段4上,即使关闭了内格尔算法(因此所有特殊密钥字节一起到达并可以一起处理),也有~0.2ms的延迟。在段6上,我们无法识别~0.2ms的延迟,因为服务器需要一段时间来重新处理和重新分组一些丢失的段的重传,但这个延迟超过2s。

F2键示例中,在段11上我们也可以注意到~0.2ms的延迟。我们无法在片段12上识别它,因为它可能是在片段11之后发送的。

这确实表明@MattTimmersans的答案是正确的,并且可能是该书对片段延迟的误解。它们可能确实是网络介质的一个特征,而不是由于数据没有被发送到TCP堆栈而导致ack延迟。

参考:http://people.na.infn.it/·加鲁菲/didattica/CorsoAcq/Trasp/Lezione9/tcpip_ill/tcp_int.htm

由于区段2,4,5,7中的ACK带有数据,因此它们不是延迟的ACK(不是由ACK计时器失效发起的)。

我相信与这些相关的0.25秒延迟只是主机之间的往返时间。我注意到这些数据记录于1993年。如果我没记错的话,我想250ms的ping在当时并不少见。

如果tcpdump在vangogh而不是slip上运行,看起来它会在ESC[M数据包之后快速看到回显。

这个例子表明Nagle的算法即使没有ACK延迟也可以很糟糕,因为它给交换增加了额外的往返时间。

最新更新