欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

TCP重传机制

程序员文章站 2022-07-08 20:07:04
...
这里的文章重传已经写的比较好的一篇,因此,直接拿来参考!
其实,重传目前在网络的应用从编程的角度来看,用的比较少!就当作一种参考的工具吧!
TCP要保证所有的数据包都可以到达,所以,必需要有重传机制
超时重传机制:就是发送端死等接收端的ack,直到发送端超时之后,在发送一个包,直到收到接收端的ack为止。
例如:接收端给发送端的Ack确认只会确认最后一个连续的包,比如,发送端发了1,2,3,4,5一共五份数据,接收端收到了1,2,于是回ack 3,然后收到了4(注意此时3没收到),此时的TCP会怎么办?等待发送端的ACK 3,直到超时后,就会再发送3.面临一个艰难的选择,就是,是重传之前的一个还是重传所有的问题。对于上面的示例来说,是重传#3呢还是重传#3,#4,#5呢?
快速重传机制:这个机制不以时间为驱动,而是以数据来重传!如果接收端包收包没有连续到达,就ACK最后那个可能被丢了的包,如果发送方连续收到接收端3次相同的ack,就重传。
例如:如果发送方发出了1,2,3,4,5份数据,第一份先到送了,于是就ack回2,结果2因为某些原因没收到,3到达了,于是还是ack回2,后面的4和5都到了,但是还是ack回2,因为2还是没有收到,于是发送端收到了三个ack=2的确认,知道了2还没有到,于是就马上重转2。然后,接收端收到了2,此时因为3,4,5都收到了,于是ack回6。
它依然面临一个艰难的选择,就是,是重传之前的一个还是重传所有的问题。对于上面的示例来说,是重传#2呢还是重传#2,#3,#4,#5呢?因为发送端并不清楚这连续的3个ack(2)是谁传回来的?

于是进一步,引入了下面的机制:从发送端入手,选择确定(SACK)(参看RFC 2018),这种方式需要在TCP头里加一个SACK的东西,ACK还是Fast Retransmit的ACK,SACK则是汇报收到的数据碎版。
TCP重传机制


这样,在发送端就可以根据回传的SACK来知道哪些数据到了,哪些没有到。于是就优化了Fast Retransmit的算法。当然,这个协议需要两边都支持。在 Linux下,可以通过tcp_sack参数打开这个功能(Linux 2.4后默认打开)。

这里还需要注意一个问题——接收方Reneging,所谓Reneging的意思就是接收方有权把已经报给发送端SACK里的数据给丢了。这样干是不被鼓励的,因为这个事会把问题复杂化了,但是,接收方这么做可能会有些极端情况,比如要把内存给别的更重要的东西。所以,发送方也不能完全依赖SACK,还是要依赖ACK,并维护Time-Out,如果后续的ACK没有增长,那么还是要把SACK的东西重传,另外,接收端这边永远不能把SACK的包标记为Ack。

注意:SACK会消费发送方的资源,试想,如果一个攻击者给数据发送方发一堆SACK的选项,这会导致发送方开始要重传甚至遍历已经发出的数据,这会消耗很多发送端的资源。详细的东西请参看《TCP SACK的性能权衡

Duplicate SACK – 重复收到数据的问题

Duplicate SACK又称D-SACK,其主要使用了SACK来告诉发送方有哪些数据被重复接收了RFC-2883 里有详细描述和示例。下面举几个例子(来源于RFC-2883

D-SACK使用了SACK的第一个段来做标志,

  • 如果SACK的第一个段的范围被ACK所覆盖,那么就是D-SACK
  • 如果SACK的第一个段的范围被SACK的第二个段覆盖,那么就是D-SACK

示例一:ACK丢包

下面的示例中,丢了两个ACK,所以,发送端重传了第一个数据包(3000-3499),于是接收端发现重复收到,于是回了一个SACK=3000-3500,因为ACK都到了4000意味着收到了4000之前的所有数据,所以这个SACK就是D-SACK——旨在告诉发送端我收到了重复的数据,而且我们的发送端还知道,数据包没有丢,丢的是ACK包。

1
2
3
4
5
6
7
Transmitted  Received    ACK Sent
Segment      Segment     (Including SACK Blocks)
 
3000-3499    3000-3499   3500 (ACK dropped)
3500-3999    3500-3999   4000 (ACK dropped)
3000-3499    3000-3499   4000, SACK=3000-3500
                                    ---------

 示例二,网络延误

下面的示例中,网络包(1000-1499)被网络给延误了,导致发送方没有收到ACK,而后面到达的三个包触发了“Fast Retransmit算法”,所以重传,但重传时,被延误的包又到了,所以,回了一个SACK=1000-1500,因为ACK已到了3000,所以,这个SACK是D-SACK——标识收到了重复的包。

这个案例下,发送端知道之前因为“Fast Retransmit算法”触发的重传不是因为发出去的包丢了,也不是因为回应的ACK包丢了,而是因为网络延时了。

1
2
3
4
5
6
7
8
9
10
11
Transmitted    Received    ACK Sent
Segment        Segment     (Including SACK Blocks)
 
500-999        500-999     1000
1000-1499      (delayed)
1500-1999      1500-1999   1000, SACK=1500-2000
2000-2499      2000-2499   1000, SACK=1500-2500
2500-2999      2500-2999   1000, SACK=1500-3000
1000-1499      1000-1499   3000
               1000-1499   3000, SACK=1000-1500
                                      ---------

 

可见,引入了D-SACK,有这么几个好处:

1)可以让发送方知道,是发出去的包丢了,还是回来的ACK包丢了。

2)是不是自己的timeout太小了,导致重传。

3)网络上出现了先发的包后到的情况(又称reordering)

4)网络上是不是把我的数据包给复制了。

 知道这些东西可以很好得帮助TCP了解网络情况,从而可以更好的做网络上的流控

Linux下的tcp_dsack参数用于开启这个功能(Linux 2.4后默认打开)

最后,由于最近学习netstat命令,下面就通过一个服务器的命令结果,看下问题:

TcpExt:
    79 invalid SYN cookies received
    8 resets received for embryonic SYN_RECV sockets
    9 packets pruned from receive queue because of socket buffer overrun
    5 ICMP packets dropped because they were out-of-window
    132513 TCP sockets finished time wait in fast timer
    5520362 delayed acks sent
    4854 delayed acks further delayed because of locked socket
    Quick ack mode was activated 265388 times
    112119042 packets directly queued to recvmsg prequeue.
    255626524 bytes directly in process context from backlog
    2657617950 bytes directly received in process context from prequeue
    141 packets dropped from prequeue
    2779493398 packet headers predicted
    108242651 packets header predicted and directly queued to user
    209035695 acknowledgments not containing data payload received
    1531587862 predicted acknowledgments
    131551 times recovered from packet loss by selective acknowledgements
    279 bad SACK blocks received
    Detected reordering 10 times using FACK
    Detected reordering 19 times using SACK
    Detected reordering 37 times using time stamp
    214 congestion windows fully recovered without slow start
    20 congestion windows partially recovered using Hoe heuristic
    3246 congestion windows recovered without slow start by DSACK
    61602 congestion windows recovered without slow start after partial ack
    TCPLostRetransmit: 31880
    12361 timeouts after SACK recovery
    289 timeouts in loss state
    498564 fast retransmits
    13967 forward retransmits
    42221 retransmits in slow start
    56690 other TCP timeouts
    TCPLossProbes: 1113019
    TCPLossProbeRecovery: 928744
    7366 SACK retransmits failed
    13 times receiver scheduled too late for direct processing
    967 packets collapsed in receive queue due to low socket buffer
    267981 DSACKs sent for old packets
    520 DSACKs sent for out of order packets
    966679 DSACKs received
    1611 DSACKs for out of order packets received
    265 connections reset due to unexpected data
    105 connections reset due to early user close
    1132 connections aborted due to timeout
    TCPDSACKIgnoredOld: 4605
    TCPDSACKIgnoredNoUndo: 723291
    TCPSpuriousRTOs: 13632
    TCPSackShifted: 701131
    TCPSackMerged: 540339
    TCPSackShiftFallback: 1468281
    TCPBacklogDrop: 7
    TCPRetransFail: 126
    TCPRcvCoalesce: 74902345
    TCPOFOQueue: 61476920
    TCPOFOMerge: 521
    TCPChallengeACK: 1636
    TCPSYNChallenge: 1600
    TCPSpuriousRtxHostQueues: 159

    Detected reordering 10 times using FACK
    Detected reordering 19 times using SACK
    Detected reordering 37 times using time stamp
      
    267981 DSACKs sent for old packets
    520 DSACKs sent for out of order packets
    966679 DSACKs received
    1611 DSACKs for out of order packets received
  从这里的统计数据可以看出,tcp对于这种数据的问题,采取上述的所有介绍的算法,
  time stamp,FACK,DSACK,SACK这些算法,了解网络失序有点帮忙,
  最终还是要抓包来看问题!
  至于其他的内容,需要等网络学习完毕之后,
  再来一一分析!