TCP差错控制

      TCP是可靠的运输层协议。这就表示应用层程序把数据流交付给TCP后要依靠TCP把整个数据流交付给另一端的应用程序,并且是按序的,没有差错、也没有任何一部分丢失或重复。

       TCP使用差错控制来提供可靠性。差错控制包括以下的一些机制:检测和重传受到损伤的报文段、重传丢失的报文段、保存失序到达的报文段直至缺失的报文到期,以及检测和丢弃重复的报文段。TCP通过三个简单的工具来完成其差错控制:检验和、确认以及超时。

检验和

       每个报文都包含了一个检验和字段,用来检查报文段是否收到损伤。如果某个报文段因检验和无效而被检查出受到损伤,就由终点TCP将其丢弃,并被认为是丢失了。TCP规定每个报文段都必须使用16位的检验和。

确认

         TCP采用确认来证实收到了报文段。控制报文段不携带数据,但需要消耗一个序号,它也需要被确认,而ACK报文段永远不需要确认【ACK报文段不消耗序号,也不需要被确认】。

       在以前,TCP只使用一种类型的确认:积累确认。目前有一些TCP实现还采用了选择确认。

      

积累确认(ACK)

       TCP最初的设计是对收到的报文段进行积累确认。接收方通告它期望接收的下一个字节的序号,并忽略所有失序到达并被保存的报文段。有时这被称为肯定积累确认或ACK。“肯定”这个词表示对于那些丢弃的、丢失的或重复的报文段都不提供反馈。在TCP首部的32位ACK字段用于积累确认,而它的值仅在ACK标志为1时才有效。

 

选择确认(SACK)

       现在越来越多的实现又增加另一种类型的确认,称为选择确认(selective acknowledgment)或SACK。SACK并没有取代ACK,而是向发送方报告了更多的信息。SACK要报告失序的数据块以及重复的报文段块(即受到不止一次)。但是,在TCP首部中并没有提供增加这类信息的地方,因此SACK是作为TCP首部末尾的选项来实现的。

产生确认

       接收方在什么时候产生?在TCP的发展历程曾经定义过好几种规则,并用在多个实现中。这里我们给出几个最常用的规则。这些规则的顺序并不代表其重要性。

      1、当A端向B端发送数据报文段时,必须包含(捎带)一个确认,它给出A端期望接收的下一个序号。这条规则减少了所需要的报文段的数量,因此也减少了通信量。

       2、当接收方没有数据要发送,但是收到了按序到达(序号是所期望的)的报文段,同时前一个报文段也已经确认过了,那么接收方就推迟发送确认报文段,直到另一个报文段到达,或经过了一段时间(通常是500ms)。换言之,如果仅仅有一个按序到达的报文段还没有被确认,接收方需要推迟发送确认报文段。这条规则也减少了ACK报文段的通信量。

       3、当具有所期望的序号的报文段到达,而且前一个按序到达的报文段还没有被确认,那么接收方就要立即发送ACK报文段。换言之,在任何时候,不能有两个以上按序到达的报文段未被确认。这就避免了不必要的重传,而不必要的重传可能会引起网络的拥塞。

       4、当序号比期望的序号还大的失序报文段到达时,接收方立即发送ACK报文段,并宣布下一个期望的报文段序号。这将导致对丢失报文段的快重传。

       5、当一个丢失的报文段到达时,接收方要发送ACK报文段,并宣布下一个所期望的序号。这是告诉接收方,被通报为丢失的报文段已经接收到了。

      6、如果到达一个重复的报文段,接收方丢弃该报文段,但是应当立即发送确认,指出下一个期望的报文段。这就解决了ACK报文段本身丢失所带来的一些问题。

重传

         差错控制机制的核心就是报文段的重传。在一个报文段发送时,它会被保存到一个队列中,直至被确认为止。当重传计时器超时,或者发送方收到该队列中第一个报文段的三个重复的ACK时,该报文段被重传。

RTO之后的重传

       发送方TCP为每一条连接设置一个重传超时(retransmission time-out,RTO)计时器。当计时器时间到(即超时),TCP发送队列中最前面的报文段(即序列号最小的报文段),并重启计时器。请注意,此处再次强调我们假设S(f) < S(n).这个版本的TCP有时被称为Tahoe。我们以后将会看到,在TCP中RTO的值是动态的,它根据报文段的往返时间(RTT)更新。RTT是一个报文段到达终点并收到它的确认所需要的时间。

三个重复的ACK报文段之后的重传

       如果RTO值不是很大,前面描述的重传报文段的规则就足够了。但是,如果允许发送方更快地重传,而不用等计数器超时,就能更有利于网络吞吐量,因此现在的大多数实现都遵守到了三个重复ACK则立即重传丢失的报文段的规则。这一特性称为快重传(fast retransmission),而使用了这个特性的TCP版本称为Reno。在这个版本中,如果针对某个报文段有三个重复的确认(即原始的ACK再加上三个完全一样的副本)到达,那么下一个报文段将立即重传,而不用等待计时器超时。

失序的报文段

       现在的TCP实现都不会丢弃失序到达的报文段,而是把这些报文段暂时保存下来,并把他们标志为失序报文段,直至缺失的报文段到齐。但是请注意,TCP从来不会把失序的报文段交付给进程。TCP保证数据必须按序交付到进程。

TCP数据传送FSM(Finite State Machine 有限状态机)

       TCP的数据传送接近于选择重传协议,同时又有那么一点像GBN的地方。因为TCP接受失序到达的报文段,因此它的行为可以被认为像SR协议一样,但是因为最初它的确认机制是积累的,这又使得它看起来像GBN。不过,如果TCP的实现中使用了SACK,那么它就更接近于SR。最适合TCP的模型是选择重传协议。

发送端FSM

       让我们用一个简化的FSM来描述TCP协议的发送端。这个FSM类似于我们为SR协议所讨论的,只是有一些专门针对TCP的修改。我们假设通信时单项的,并且报文段使用ACK来确认。就目前而言,我们还忽略了选择确认和拥塞控制的问题。下图描绘了TCP发送端的简化FSM,请注意,这个FSM只是最基本的,它不包括诸如糊涂窗口综合征(Nagle算法)和窗口关闭等问题。因为它定义的是单向通信,所以也忽略了所有涉及到双向通信的内容。

       上面这个FSM和我们讨论的SR协议时的FSM有一些区别。其中的一个区别是快重传(三个重复的ACK规则),另一个区别就是窗口大小要根据rwnd的值来调整(目前先忽略拥塞控制的问题)。

接收端FSM

      让我们用一个简化的FSM来描述TCP协议的接收端。这个FSM类似于我们讨论的SR协议的状态机。只是有一些专门针对TCP的修改。我们假设通信时单向的,并且报文段使用ACK来确认。在此处我们还忽略了选择确认和拥塞控制的问题。下图描绘了TCP接收端的简化FSM。请注意,我们忽略了诸如糊涂窗口综合征(Clark 算法)和窗口关闭等问题。

        同样,这个FSM和我们讨论的SR协议时的FSM也有一些区别。其中一个区别是单向通信的ACK推迟技术。另一个区别是重复ACK的发送,以允许发送方实施快重传策略。

        我们还要强调的是双向通信时接收方的FSM就不像SR协议那么简单了。我们需要考虑到其他一些策略,譬如接收方有数据要返回时,它就要发送一个及时的ACK。

举例

      我们将举例一些在TCP的操作中出现的涉及到差错控制问题的情况。在这些情况中,我们用长方形表示报文段。如果报文段携带有数据,就给出字节编号的范文和确认字段值。如果报文段仅携带确认,就在一个小长方形中给出确认号。

正常运行

       第一种情况是两个系统之间的双向数据传送,如下图,客户端TCP发送了一个报文段,服务TCP发送了三个报文段。图中还指出了每一个确认所遵循的规则。对于客户的第一个报文段和服务器的所有三个报文段,遵循前面给出的规则1.因为报文段中有要发送的数据,所以显示出下一个期望的字节号。当客户收到服务器的第一个报文段时,它没有更多的数据要发送,因此只发送一个ACK报文段。但是,根据规则2,这个确认需要推迟500ms,以便于观察是否还有更多的报文段到达,而它不能永远推迟确认。当下一个报文到达时,设置另一个ACK推迟计时器。不过,在这个计时器超时之前,第三个报文段到达了。根据规则3,第三个报文段的到达触发了另一个确认。我们没有显示RTO计时器,因为没有报文丢失或延迟。我们就坚定RTO计时器一切正常。

报文段丢失

       在这种情况下,我们要给出当一个报文段丢失或受到损伤时会发生什么。接收方对待丢失的报文段和受到损伤的报文是一样的。丢失的报文就是在网络的某个角落被丢弃了,而损伤的报文是接收方自己丢弃的,这两种情况都被认为是丢失。下图描绘了有一个报文丢失的情况(也许是因网络拥塞而被网络中的某个路由器丢弃了)。

         我们这里假定数据传送时单向的:一端发送,另一端接收。在这种情况下,发送方发送了报文段1和2,然后立即被一个ACK确认(规则3)。但是报文段3丢失了,接收方收到报文段4,顺序被打乱。接收方把这个报文段中的数据在缓存中存储下来,并且留出空隙,表明这里的数据时不连续的。接收方立即向发送方发送确认,并指出期望的下一个字节(规则4)。请注意,接收方将字节801-900保存起来,但在间隙被填入之前绝不会把这些字节交付给应用程序。

       发送方TCP在整个连接期间只保持一个RTO计时器。当第三个报文段超时后,发方TCP就重传报文3,这次它到达了接收方,并被正常地确认(规则5)。

快重传

       在这种情况下,我们想描绘一下快重传。这种情况与第二种情况基本一样,不同的只是RTO具有更大的数值,见下图。

          当接收方收到第四个,第五个和第六个报文段时,都触发了确认(规则4)。发送方收到四个具有相同的确认(其中三个是重复的)。虽然计时器还没有到期,但是根据快重传的规则,要求立刻重传报文段3,也就是说所有这些重复的确认所期望的报文段。在重传报文段3之后,计时器重启。

延迟的报文段

       第四种情况突出了延迟的报文段。TCP使用IP的服务,而IP是无连接的协议。每个封装有TCP报文段的IP数据报可能走的不同的路由,以不同的时延到达最后的重点。因此,TCP报文段就有可能被延迟。延迟的报文段有时可能会超时。如果延迟的报文段在它的重传报文段到达之后才到达,会被认为是重复的报文段而被丢弃。

重复的报文段

       发送方TCP有可能产生重复的报文段,例如:当一个延迟的报文段被接收方误认为丢失时,发送方会这样做。终点TCP对这种重复的报文段的处理是一个很简单的过程。终点TCP期望的字节流是连续的。当一个报文段到达时,若其序号等于过去已经接收并保存的字节序号,则把这个报文段丢弃。此时需要发送一个ACK,它的ackNo指向所期望的报文段。

自动纠正丢失的ACK

       这种情况要说明的是一个丢失的确认中的信息会包含在下一个确认中,这也是使用累积确认最重要的优点。下图描绘了数据的接收方发送的一个确认丢失了。在TCP确认机制中,一个丢失的确认甚至可能不会被源点TCP注意到。TCP使用积累的确认系统。我们说,下一个确认自动地纠正了丢失确认带来的影响。

丢失的确认被重传的报文段纠正

       下图描绘了确认丢失的情况。

        如果下一个确认推迟了很长一段时间,或者没有下一个确认(丢失的确认就人最后一个),那么RTO计时器就会触发纠正过程,其结果就是一个重复的报文段。当接收方收到这个重复的报文段是,就丢弃它,但是要理解重传上次的ACK来通知发送方这个报文段(或几个报文段)已经收到了。

        请注意,图中虽然有两个报文段没有被确认,但只重传了一个报文段。当发送方收到了重传的ACK时,就知道这两个报文段都已经安全完好地传送到对方了,因为确认是积累的。

因确认丢失而产生死锁

        也可能有这样一种情况,确认的丢失可能会引起系统的死锁。当接收方发送了确认,同时把rwnd置为0,也就是请求发送方暂时关闭其窗口时,就会出现这种情况。过了一段时间之后,接收方打算取消这一限制,但是如果它没有数据要发送,就会发送ACK报文段,并且用rwnd等于一个非零的数值来取消这个限制。如果这个确认丢失了,那么就会产生问题。发送方一直在等待确认来宣布非零的rwnd。而接收方则认为发送方已经收到了这个确认,因而在等待数据。这种情况叫作死锁(deadlock),也就是双方都在等待对方的响应,而什么也不会发生。此时重传计时器并没有设置。要避免死锁,就要设计一个持续计时器,如果处理不当,丢失的确认可能会产生死锁。

猜你喜欢

转载自woodding2008.iteye.com/blog/2341360