TCP使用差错控制来提供可靠性。差错控制包括以下的一些机制:检测和重传受到损伤的报文段、重传丢失的报文段、保存失序到达的报文段直至缺失的报文到期,以及检测和丢弃重复的报文段。TCP通过三个简单的工具来完成其差错控制:检验和、确认以及超时。
每个报文都包含了一个检验和字段,用来检查报文段是否收到损伤。如果某个报文段因检验和无效而被检查出受到损伤,就由终点TCP将其丢弃,并被认为是丢失了。TCP规定每个报文段都必须使用16位的检验和。
控制报文段不携带数据,但需要消耗一个序号,它也需要被确认,而ACK报文段永远不需要确认,ACK报文段不消耗序号,也不需要被确认。在以前,TCP只使用一种类型的确认:积累确认。目前有一些TCP实现还采用了选择确认。
接收方通告它期望接收的下一个字节的序号,并忽略所有失序到达并被保存的报文段。有时这被称为肯定积累确认或ACK。在TCP首部的32位ACK字段用于积累确认,而它的值仅在ACK标志为1时才有效。
选择确认(selective acknowledgment)或SACK。SACK并没有取代ACK,而是向发送方报告了更多的信息。SACK要报告失序的数据块以及重复的报文段块。SACK是作为TCP首部末尾的选项来实现的。
1、当A端向B端发送数据报文段时,必须包含(捎带)一个确认,它给出A端期望接收的下一个序号。
2、当接收方没有数据要发送,但是收到了按序到达(序号是所期望的)的报文段,同时前一个报文段也已经确认过了,那么接收方就推迟发送确认报文段,直到另一个报文段到达,或经过了一段时间(通常是500ms)。
3、当具有所期望的序号的报文段到达,而前一个按序到达的报文段还没有被确认,那么接收方就要立即发送ACK报文段。
4、当序号比期望的序号还大的失序报文段到达时,接收方立即发送ACK报文段,并宣布下一个期望的报文段序号。这将导致对丢失报文段的快重传。
5、当一个丢失的报文段到达时,接收方要发送ACK报文段,并宣布下一个所期望的序号。
6、如果到达一个重复的报文段,接收方丢弃该报文段,但是应当立即发送确认,指出下一个期望的报文段。
差错控制机制的核心就是报文段的重传。在一个报文段发送时,它会被保存到一个队列中,直至被确认为止。当重传计时器超时,或者发送方收到该队列中第一个报文段的三个重复的ACK时,该报文段被重传。
发送方TCP的计时器时间到,TCP发送队列中最前面的报文段(即序列号最小的报文段),并重启计时器。在TCP中RTO的值是动态的,它根据报文段的往返时间(RTT)更新。TCP整个连接期间只有一个RTO计时器
三个重复ACK则立即重传丢失的报文段的规则。这一特性称为快重传(fast retransmission),如果针对某个报文段有三个重复的确认(即原始的ACK再加上三个完全一样的副本)到达,那么将立即重传,并重启RTO计时器,而不用等待计时器超时。
4、相关:
现在的TCP不会丢弃失序到达的报文段,而是暂时保存下来,并把他们标志为失序,直至缺失的报文段到齐。但是请注意,TCP从来不会把失序的报文段交付给进程。TCP保证数据必须按序交付到进程。
FAQ1:TCP如何检测数据报是否损伤?
TCP接收端在收到报文段后,在报文段的首部加上伪首部后,连同数据部分一块计算检验和,结果为全1,数据没收到损伤
FAQ5:发送端与接收端何时可能产生死锁现象?TCP是如何解决该死锁僵局的?
当接收端向发送端发送一个零窗口报文段(报文段首部中窗口值的大小设置为0),发送端就停止向接收端发送报文段。后来接收端想通知发送端,让其接着发送数据时,便向发送端发送一个非零窗口报文段。该报文段在路上丢失了。而接收端以为该非零报文段已经发送给发送端,。而发送端由于没有收到接收方的非零窗口报文段,于是两端都等待,陷入“死锁”的状态。
TCP为发送端设置一个持续计时器,当发送端收到零窗口报文段时,启动该持续计时器,便等待着接收方的非零通知。持续计时器又超时时还没收到,发送端便向接收方发送一个“探测报文段”,该探测报文仅携带1B的数据,该探测报文会消耗一个序号,但特殊的是,该探测报文段的序号永远不需要确认。目的是促使TCP接收端重传一个确认,该确认的内容包括接收端希望发送端的发送窗口的大小作为回复,即:如果接收端希望发送端的窗口大小仍然是0,重置续计时器;如果不是0,则该僵局打破了。
注意:即便发送发收到了零窗口设置的报文段,发送端也能接收这几种报文段:探测报文段、ACK报文段和携带紧急数据的报文段。