TCP可靠传输的实现
这篇文章我们来学习TCP可靠传输的实现。
为了方便讨论,我们假定数据传输只在一个方向进行,即A发送数据,B给出确认。这样的好处是使讨论限于两个窗口,即发送方A的发送窗口和接收方B的接收窗口。
以字节为单位的滑动窗口
TCP的滑动窗口是以字节为单位的。现假定A收到了B发来的确认报文段,其中窗口是10字节(B的接收窗口是10字节),而确认号是28(表明B期望收到的下一个序号是28,而序号28为止的数据已收到了)。根据这两个数据,A就构造出自己的发送窗口,如下图所示:
先讨论发送方A的发送窗口。发送窗口表示:在没有收到B的确认的情况下,A可以连续把窗口内的数据都发送出去。凡是已经发送过的数据,在未收到确认之前都必须暂时保留,以便在超时重传时使用。发送窗口里面的序号表示允许发送的序号。显然,窗口越大,发送方就可以在收到对方确认之前连续发送更多的数据,因而可能获得更高的传输效率。A的发送窗口一定不能超过B的接收窗口数值(发送方的发送窗口大小还要受到当时网络拥塞程度的制约,但目前,暂不考虑)。
发送窗口后沿的后面部分表示已发送且已收到了确认,这些数据显然不需要再保留了。发送窗口前沿的前面部分表示不允许发送的,因为接收方都没有为这部分数据保留临时存放的缓存空间。
发送窗口的位置由窗口前沿和后沿的位置共同确定。发送窗口后沿的变化情况有两种可能,即不动(没有收到新的确认)和前移(收到了新的确认)。发送窗口后沿不可能向后移动,因为不能撤销掉已收到的确认。发送窗口前沿通常是不断向前移动,但也有可能不动(两种情况,一是没有收到新的确认,发送窗口的大小不变;二是收到了新的确认但对方通知的窗口缩小了,使得发送窗口前沿正好不动)。
现在假定A发送了序号为28~34的数据。这时,发送窗口位置并未改变,但发送窗口内靠后面有7个字节表示已发送但未收到确认。而发送窗口内靠前面的3个字节是允许发送但尚未发送的。
要描述一个发送窗口的状态需要三个指针:P1, P2, P3。指针都指向字节的序号,意义如下:
(1)小于P1的是已发送并已收到确认的部分,大于P3的是不允许发送的
(2)P3 - P1为A的发送窗口
(3)P2 - P1为已发送但尚未收到确认的字节数
(4)P3 - P2为允许发送但当前尚未发送的字节数(又称为可用窗口或有效窗口)
再看一下B的接收窗口。B的接收窗口大小是10。在接收窗口外面,到28号为止的数据是已经发送过确认,并且已经交付主机了。因此B可以不再保留这些数据。接收窗口内的序号(28~37)是允许接收的。在下图中,B收到了序号为29和30的数据。这些数据没有按序到达,因为序号为28的数据没有收到(也许丢失了,也许滞留在网络中的某处)。请注意,B只能对按序收到的数据中的最高序号给出确认,因此B发送的确认报文段中的确认号仍然是28,而不是29或30。
现在假定B收到了序号为28的数据,并把序号为28~30的数据交付主机,然后B删除这些数据,接着把接收窗口向前移动3个序号,同时给A发送确认,其中窗口值仍为10,但确认号是31。这表明B已经收到了序号31为止的数据。此时B还收到了序号为32, 33的数据,但这些都没有按序到达,只能先暂存在接收窗口中。A收到B的确认后,就可以把发送窗口向前滑动3个序号,但指针P2不动。可以看出,现在A的可用窗口增大了,可发送的序号范围是35~40。
如下图所示,A在继续发送完序号35~40的数据后,指针P2向前移动和P3重合。发送窗口内的序号都已用完,但还没有收到确认。由于A的可用窗口减小到0,因此必须停止发送。A在经过一段时间后(由超时计时器控制),就重传这部分数据,重新设置计时器,直到收到B的确认为止。如果A收到确认号落在发送窗口内,那么A就可以使发送窗口继续向前滑动,并发送新的数据。请注意,在没有收到B的确认时,为了保证可靠传输,A只能认为B还没有收到这些数据。
前文说过,发送方的应用进程把字节流写入TCP的发送缓存,接收方的应用进程从TCP的接收缓存中读取字节流。下面进一步讨论窗口和缓存的关系。
如下图所示,画出了发送方维持的发送缓存和发送窗口:
发送缓存用来暂时存放:
(1)发送应用程序传送给发送方TCP准备发送的数据
(2)TCP已发送但尚未收到确认的数据
发送窗口通常只是发送缓存的一部分。已被确认的数据应当从发送缓存中删除,因此发送缓存和发送窗口的后沿是重合的。发送应用程序最后写入发送缓存的字节减去最后被确认的字节,就是还保留在发送缓存中的被写入的字节数。发送应用程序必须控制写入缓存的速率,不能太快,否则发送缓存就会没有存放数据的空间。
如下图所示,画出了接收方维持的接收缓存和接收窗口:
接收缓存用来暂时存放:
(1)按序到达的,但尚未被接收应用程序读取的数据
(2)未按序到达的数据
如果收到的分组被检测出有差错,则要丢弃。如果接收应用程序来不及读取收到的数据,接收缓存最终就会被填满,使接收窗口减小到0。反之,如果接收应用程序能够及时从接收缓存中读取收到的数据,接收窗口就可以增大,但最大不能超过接收缓存的大小。
根据以上的讨论,强调以下三点:
(1)虽然A的发送窗口是根据B的接收窗口设置的,但在同一时刻,A的发送窗口并不总是和B的接收窗口一样大。这是因为通过网络传送窗口值需要经历一定的时间滞后,发送方A还可能根据网络当时的拥塞情况适当减小自己的发送窗口数值
(2)对于不按序到达的数据应如何处理,TCP标准并无明确规定。如果接收方把不按序到达的数据一律丢弃,那么接收窗口的管理将会比较简单,但这样做对网络资源的利用不利,因为发送方会重复传送较多的数据。因此TCP通常对不按序到达的数据是先临时存放在接收窗口中,等到字节流中所缺少的字节收到后,再按序交付上层的应用进程。
(3)TCP要求接收方必须有累计确认的功能,这样可以减小传输开销。接收方可以在合适的时候发送确认,也可以在自己有数据要发送时把确认信息顺便捎带上。但注意两点,一是接收方不应过分推迟发送确认,否则会导致发送方不必要的重传,这反而浪费了网络的资源。TCP标准规定,确认推迟的时间不应超过0.5秒。若收到一连串具有最大长度的报文段,则必须每隔一个报文段就发送一个确认。二是捎带确认实际上并不经常发生,因为大多数应用程序很少同时在两个方向上发送数据。