5.7 TCP可靠传输的实现
# 5.7 TCP可靠传输的实现
本节课我们介绍TCP可靠传输的实现,
TCP基于以字节为单位的滑动窗口来实现可靠传输。我们来举例说明,这是因特网上的两台主机,他们之间已经建立了一个TCP连接,为了简单起见,我们假定数据传输只在一个方向进行,换句话说,发送方给接收方发送TCP数据报文段,接收方给发送方发送相应的TCP确认报文段,这样的好处是使讨论仅限于两个窗口,也就是发送方的发送窗口和接收方的接收窗口。
TCP的滑动窗口是以字节为单位的,如图所示,这是发送方代发送数据字节的序号。为了方便讲解和在有限的屏幕上显示更多的字节序号,字节序号的值都取的很小。现在假设发送方收到了一个来自接收方的确认报文段,在报文段首部中的窗口字段的值为20,也就是接收方表明自己的接收窗口的尺寸为20字节,确认号字段的值为31,这表明接收方希望收到下一个数据的序号是31,而序号30为止的数据已经全部正确接收了。
因此发送方根据这两个字段的值,构造出自己的发送窗口,如图所示。为了简单起见,我们假定网络不存在拥塞问题,也就是发送方在构造自己的发送窗口时,仅考虑接收方的接收窗口,而不考虑拥塞窗口。由于本例中接收方告诉发送方自己的接收窗口尺寸为20,因此发送方将自己的发送窗口尺寸也设置为20,发送方在没有收到接收方确认的情况下,可以把发送窗口内的数据依次全部发送出去,凡是已经发送过的数据,在未收到确认之前都必须暂时保留,以便在超时重传时使用。这是发送窗口的后沿,这是发送窗口的前沿,发送窗口后沿的后面部分是已发送并已收到确认的数据字节的序号,这些数据字节显然不需要再保存在发送缓存中了,可以将它们删除。
发送窗口前沿的前面部分是当前不允许发送的数据字节的序号。我们来看看发送窗口后延的移动情况,有以下两种可能,一种是不动,也就是没有收到新的确认,发送窗口的后延不会移动,另一种是向前移动,也就是收到了新的确认,发送窗口的后延向前移动。发送窗口的后沿不可能向后移动,因为不能撤销掉已收到的确认。
再来看发送窗口前沿的移动情况,有以下三种可能,通常情况下发动窗口的前沿是不断向前移动的,但也有可能不动。一种是由于没有收到新的确认,接收方通知的窗口大小也没有改变,另一种是收到了新的确认,可向前移动相应位置,但接收方通知的窗口缩小了,前沿应该向后回缩,如果向前移动和向后回缩的尺寸恰好相等,就会使得发送窗口的前沿不动,
发送窗口的前沿还可能向后收缩,这发生在接收方通知的窗口变小了,但TCP标准强烈不赞成这样做,因为很可能发送方在收到这个通知之前,就已经发送了窗口中的许多数据,现在又要收缩窗口,不让发送这些数据,显然就会产生错误。
现在假定发送方将发送窗口内序号31~41的数据,封装在几个不同的报文段中发送出去,此时发送窗口的位置并没有改变,发送窗口内序号31~41的数据已经发送,但未收到确认,而序号42~50的数据是允许发送,但还未发送的。请同学们思考一下,我们如何描述发送窗口的状态,换句话说,如果我们要编程实现滑动窗口机制,那么对于发送窗口的状态,应该如何标记和维护呢?如图所示可以使用三个指针,P1、P2、P3分别指向相应的字节序号,这样小于P1的,就是已发送并已收到确认的部分,大于等于P3的是不允许发送的部分。
P3减P1可以得出当前发送窗口的尺寸,
P2-P1可以得出已发送,但尚未收到确认的字节数量,
P3-P2可以得出允许发送,但当前尚未发送的字节数量。
我们再来看看接收方的接收窗口,它的尺寸为20,在接收窗口外面到30号为止的数据是已经发送过相应确认,并已交付给应用进程的数据,因此无需再保留这些数据,可将他们从接收缓存中删除了。接收窗口内31~50号数据是允许接收的数据,接收窗口外51号及其后续数据目前不允许接收。假设发送方之前发送的封装有32和33号数据的报文段到达了接收方,由于数据序号落在接收窗口内,所以接收方接受他们,并将他们存入接收缓存,但是他们是未按序到达的数据,因为31号数据还没有到达,这有可能是丢了,也有可能是滞留在网络中的某处。请注意接收方只能对按序收到的数据中的最高序号给出确认,因此接收方发出的确认报文段中的确认序号仍然是31,也就是希望收到31号数据,窗口字段的值仍是20,表明接收方没有改变自己接收窗口的大小,发送方收到该确认报文段后,发现这是一个针对31号数据的重复确认,就知道接收方收到了未按序到达的数据,由于这是针对31号数据的第一个重复确认因此,这并不会引起发送方针对该数据的快重传。另外接收方通知的窗口尺寸仍是20,因此发送方保持自己的发送窗口尺寸为20,现在假设封装有31号数据的报文段到达了接收方,方接受该报文段,将其封装的31号数据存入接收缓存,接收方现在可将接收到的31~33号数据交付给应用进程
然后将接收窗口向前移动三个序号,并给发送方发送确认报文段。该确认报文段中窗口字段的值仍为20,表明接收方没有改变自己接收窗口的大小,确认号字段的值为34,这表明接收方已经收到了序号33为止的全部数据。
现在假设又有几个数据,报文段到达了接收方,他们封装有37 38以及40号数据,这些数据的序号虽然落在接收窗口内,但他们都是未按需到达的数据,只能先暂存在接收缓存中。假设接收方先前发送的确认报文段到达了发送方,发送方接收后,将发送窗口向前滑动三个序号,发送窗口的尺寸保持不变,这样就有新序号51~53落入发送窗口内,而序号31~33移出了发送窗口,现在可将31~33号数据从发送缓存中删除了,因为已经收到了接收方针对他们的确认
发送方继续将发送窗口内,序号42~53的数据封装在几个不同的报文段中发送出去,现在发送窗口内的序号已经用完了,发送方在未收到接收方发来确认的情况下,不能再发送新的数据,序号落在发送窗口内的已发送数据,如果迟迟收不到接收方的确认,则会产生超时重传。
接下来我们还要对TCP可靠传输的实现做几点补充说明。
第一点,虽然发送方的发送窗口是根据接收方的接收窗口设置的,但在同一时刻发送方的发送窗口并不总是和接收方的接收窗口一样大,这是因为网络传送窗口值需要经历一定的时间滞后,并且这个时间还是不确定的。另外发送方还可能根据网络当时的拥塞情况,适当减小自己的发送窗口尺寸。
第二点,对于不按需到达的数据应如何处理?TCP并无明确规定,如果接收方把不按序到达的数据一律丢弃,那么接收窗口的管理将会比较简单,但这样做对网络资源的利用不利,因为发送方会重复传送较多的数据,TCP通常对不按序到达的数据是先临时存放在接收窗口中,等到字节流中所缺少的字节收到后,再按序交付上层的应用进程。
第三点,TCP要求接收方必须由累计确认和捎带确认机制,这样可以减小传输开销。接收方可以在合适的时候发送确认,也可以在自己有数据要发送时把确认信息顺便捎带上,但是接收方不应过分推迟发送确认,否则会导致发送方不必要的超时重传,这反而浪费了网络资源。
TCP标准规定,确认推迟的时间不应超过0.5秒。若收到一连串具有最大长度的报文段,则必须每隔一个报文段就发送一个确认。另外捎带确认实际上并不经常发生,因为大多数应用程序很少同时在两个方向上发送数据。
第4点,TCP的通信是全双工通信,通信中的每一方都在发送和接收报文段,因此每一方都有自己的发送窗口和接收窗口,在谈到这些窗口时,一定要弄清楚是哪一方的窗口。
最后我们来做两个相关的习题,这是计算机专业考研,全国统考,计算机网络部分,2009年的题38 答案是选项D 这是主机甲给主机乙发送的第一个数据报文段,其首部中序号字段的值为200,也就是说其数据载荷的第一个字节的序号为200,由于数据载荷共有300个字节,因此数据载荷的最后一个字节的序号为499,
这是主机甲给主机乙发送的第二个数据报文段的数据载荷部分。由于前一个报文段数据载荷的最后一个字节的序号是499,因此本报文段数据载荷的第一个字节的序号应为500,相应的写首部中序号字段的值应为500,由于数据载荷共有500个字节,因此数据载荷的最后一个字节的序号为999,
这是主机乙给主机甲发送的确认报文段的首部,其中确认序号字段的值为1000,这是因为主机已目前已经按序正确接收了序号到999为止的全部数据,现在希望接收序号为1000及其后续的数据,该确认报文段实际上是对已正确接收的两个TCP数据报文段的累计确认。
再来看2011年的题40,答案是选项B。我们可以画出如下所示的示意图,题目要求我们回答主机已发送给主机甲的确认序号是多少?我们从已知的第三个数据报文段首部中序号字段的值为900还是分析,这就表明其数据载荷的第一个字节的序号为900,由于数据载荷共有500个字节,因此数据载荷的最后一个字节的序号为1399。根据第三个数据报文段,数据载荷的第一个字节的序号为900可以得出,第二个数据报文段数据载荷的最后一个字节的序号为899,由于数据载荷共有400个字节,因此数据载荷的第一个字节的序号为500,相应的第二个数据报文段首部中序号字段的值应为500。
根据第二个数据报文段数据载荷的第一个字节的序号为500,可以得出第一个数据报文段数据载荷的最后一个字节的序号为499,由于数据载荷共有300个字节,因此数据载荷的第一个字节的序号为200,相应的第一个数据报文段首部中序号字段的值应为200,由于TCP规定只能对按序到达的最高序号进行确认,因此主机乙发送到确认报文段,实际上是对第一个数据报文段的确认。由于第一个数据报文段数据载荷的最后一个字节的序号为499,因此针对该序号的确认序号应为500,表明序号499为止的全部数据已经收到了,现在希望接收500号及其后续数据。
本节课的内容小结如下