TCP中的序号和确认号

在上一节讲到TCP报文段结构的时候讲到TCP首部中有两个字段序号和确认号,这一节里面我们就详细的讲讲TCP序号和确认号是如何在TCP的可靠数据传输服务中发挥重要作用的。在我们实际了解序号和确认号之前我们先要了解一下可靠数据传输原理。

可靠数据传输原理

TCP协议是运行在网络层IP协议之上的,这里我们需要直接给出的是:IP协议提供了单一的服务,称为尽力而为服务,数据在IP协议上传输是不可靠的。我们要研究可靠数据传输,那么就需要知道数据直接在IP协议上传输怎么就不可靠了呢?会出现那些问题呢?数据在网络中传输会出现下列问题:

  • 比特错误
  • 分组丢失
  • 接收顺序错乱

可靠数据传输的目的就是要解决上面的三个问题。

比特错误

对于比特错误,TCP首部中有检验和字段,接收方在接收到数据之后,通过检查检验和字段就可以判断传输的数据中是否发生了比特错误。如果发生了比特错误,接收方可以进行纠错,如果纠错无法恢复那么接收方就只能给发送方以反馈,把这个消息告诉发送方。发送方这时就会重传该分组。

上面描述中的给发送方以反馈的过程就是发送一个ACK分组,即确认的过程(事实上,无论接收到的分组是否出现了错误,接收方都应该给发送方以反馈,让发送方时刻知道当前数据发送是否正常。也就是说每发送一个分组,发送方和接收方之间都会有一个确认的过程)。而发送方重传则是当传输出现问题时的唯一解决办法。

这里我们忽略了一个问题,确认分组(ACK分组)也可能会发生比特错误。这时候该怎么办呢?首先我们要明白的一点是确认分组也是一个TCP报文段,也包含了检验和字段,也就是说发送方可以根据这个检验和字段判断出该确认分组是否出现了比特错误。基于这一点,这个问题或许有下面的三中解决办法:

1.当发送方接收到发生比特错误的确认分组时,需要向接收方也发过去一个类似确认分组的分组,以告诉接收方重新发送确认分组。但是这个类似于确认分组的分组也是可能会发生比特错误的。这这样的话我们明显陷入了一个循环问题,如果选择这种方法,无疑我们走上了一条困难重重之路。这种方法显然是行不通的。

2.第二种可能的方法是增加足够的检验和比特,使发送方不仅可以检测到比特错误,还可以自动纠正比特错误。这种方法对于会产生比特错误,但是不会丢失数据的情况来说是可以直接解决问题的。但真实的情景中丢失数据是存在的。所以这种方法在真实的情景中也是无能为力。

3.第三种方法是,当发送方检查到表明传送成功的确认分组和检查到由于比特错误而意义含糊不清的确认分组时,都重传最近分组。这种方法逻辑很清晰,但是却引入了一个新问题。由于比特错误而意义含糊不清的确认分组里面有一部分是表明传送成功,还有一部分是表明传送失败,而发送方都将其看做是传送失败的意思,从而重传最近分组。这样就在网络传输中引入了冗余分组。接收方不知道接收到的分组是新的还是一次重传。也就是说,接收方可能会两次或以上的接收到同一个分组,并且将他们当做新的分组处理。这样的话,发送方发送的数据与接收方接收到的数据就不一致,所以这也违背了可靠数据传输的原则。

解决这一个问题其实也很简单,我们只需要对第三种方法稍加改进即可。也就是在数据分组中添加一个新字段,让发送发对其数据分组编号,即将发送发数据分组的序号放入该分组中。于是,接收方只需要检查序号即可确定收到的分组是否是一次重传。(几乎所有现有的数据传输协议中,包括TCP,都采用了这种方法)

分组丢失

下面我们来考虑分组丢失的问题。在丢失分组这个层面来讲,发送方将一个分组发送出去有可能发生发送的分组丢失或者接收方发送给发送方的用于确认的分组丢失,如下:

上面的两种丢失情况,对于发送方来说是一样的--接收不到确认分组。所以发送方采取的措施也是统一的--重传。

但是,这里我们还需要考虑一些细节。发送方怎样确认自己接收不到一个分组的确认分组的呢?发送方需要等待足够长的时间以便确认分组丢失。显然发送方至少需要等待一个这样长的时间:即发送方与接收方之间的一个往返时延加上接收方处理一个分组的时间。在许多网络环境下,最欢情况下的最大时延是很难估算的,确定的因素非常少。在实际的处理是发送方明智的选择一个时间 值,以判定可能发生了丢包(尽管不能确保),如果在这个时间内没有收到ACK,则重传该分组。这就是TCP的超时重传机制。注意到如果一个分组经历了一个特别大的时延,发送方可能会重传该分组,即使该数据分组及其ACK都没有丢失。这样就可能在发送方与接收方的信道中引入冗余数据分组,好在,上面讲到的序号可以成功解决这一个问题。

从发送方的观点看,重传是一种万能灵药。发送方不知道是一个数据分组丢失,还是一个ACK丢失,或者只是该分组或ACK过度时延。在所有这些情况下,动作都是一样的:重传。为了实现基于时间的重传机制,我们需要一个倒计时定时器

接收顺序错乱

经过上面的讨论,解决接收顺序错乱也就简单了,依然是序号,接收方有了每一个分组的需要,那么不发生数据错误和数据丢失就可以装配出完整的数据。

序号和确认号的工作细节

在了解了可靠数据传输所采取的一般策略之后,我们就来仔细看看序号和确认号具体是怎样协同工作的。TCP报文段首部中两个最重要的字段就是序号字段和确认字段,这两个字段是TCP可靠传输服务的关键部分。

TCP把数据看做看成一个无结构的、有序的字节流。序号是建立在传送的字节流上,而不是建立在传送的报文段的序列之上。因此,一个报文段的序号是该报文段首字节的字节流编号

假设主机A上的一个进程想通过一条TCP连接向主机B上的一个进程发送一个数据流。主机A中的TCP将隐式的对数据流中的每一个字节编号。假定数据流由一个包含500000字节的文件组成,其MSS为1000字节,数据流的首字节编号是0。如图所示,该TCP将为该数据流构建500个报文段,给第一个报文段分配序号0,第二个报文段分配序号1000,第三个报文段分配序号2000,以此类推。每一个序号被填入到相应TCP报文段首部的序号字段。

确认号要比序号要难处理一些。主机A填充进报文段的确认号是主机A期望从主机B收到的下一个字节的序号。看一个例子有助于实际发生的事情。假设主机A已收到了来自主机B的编号为0–535的所有字节,同时假设它打算发送一个报文段给主机B。主机A等待主机B的数据流中字节536及之后的所有字节。所以主机A就会在它发往主机B的报文段的确认号字段中填上536.

结论

  • 数据在网络中传输会出现:比特错误、分组丢失、接收顺序错乱三种错误
  • 序号是建立在传送的字节流上,而不是建立在传送的报文段的序列之上。因此,一个报文段的序号是该报文段首字节的字节流编号
  • 主机A填充进报文段的确认号是主机A期望从主机B收到的下一个字节的序号