TCP协议(二)

上文我们简单的讲了下TCP报头以及连接管理机制,接下来我们继续说说其他的保证可靠性的机制。
确认应答(ACK)机制

TCP将每一个字节的数据都进行了编号,即为***。而每一个ACK都带有对应的确认***,意思是告诉发送者,我已经收到了哪些数据,下一次你从哪里开始发。

TCP协议(二)

由图分析:当主机1给主机2发送了1~1000这么多数据时,主机2如果收到了就会给主机1应答(ACK报文段,每一个ACK都带有对应的确认***),表示你给我发的1~1000的数据我已经全部收到了(收到哪些数据),下次你再给我发就给我从1001数据开始发(下次从哪里开始发)。那么主机1收到应答之后就知道对方已经收到了1~1000的全部数据,所以再一次发送数据的时候他就会从1001开始发,后面都是依此类推的情况。

当然了,当我们的主机1给主机2发送了数据之后,经过一端时间主机1并没有收到主机2的应答的情况也是有的,所以这个时候为了确保数据的准确到达,TCP就有了超时重传机制。

超时重传机制

TCP协议(二)
如图所示,主机A发送数据给主机B之后,可能因为网络拥堵等原因,数据无法到达主机B;那么主机A在一个特定的时间间隔内没有收到B发来的确认应答就会进行重发。

但是,主机A未收到主机B发来的确认应答,也可能是因为ACK丢失了:

![在这里插入图片描述](https://img-blog.****img.cn/20190126212001204.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzQxMDkxMzcz,size_16,color_FFFFFF,t_70)

因此主机B会收到很多重复数据,那么TCP协议需要能识别出哪些包是重复的包,并且把重复的丢弃掉,这时候我们可以利用前面提到的***就可以很容易做到去重的效果。

那么,超时的时间如何确定呢?

最理想的情况下,找到一个最小的时间,保证“确认应答一定能在这个时间内返回”,但是这个时间的长短随着网络环境的不同是有差异的。如果超时时间设的太长会影响整体的重传效率,如果超时时间设的太短有可能会频繁发送重复的包。

TCP为了保证无论在任何环境下都能比较高性能的通信,因此会动态的计算这个最大超时时间。

Linux下,超时以500ms为一个单位进行控制,每次判定超时重发的超时时间都是500ms的整数倍。如果重发一次仍然得不到应答,那么等待2*500ms后再进行重传;如果还得不到应答,等待4*500ms进行重传···依次类推,以指数形式递增。在累计到一定的重传次数后,TCP会认为网络或者对端主机出现异常,强制关闭连接。

滑动窗口

刚才我们讨论了确认应答机制,对每一个发送的数据段,都要给一个ACK确认应答,收到ACK后再发送下一个数据段。这样做有一个比较大的缺点:性能较差。尤其是数据往返时间较长时候。

TCP协议(二)

这样一发一收的方式性能较低,那么我们一次发送多条数据,就可以大大的提高性能(其实是将多个段的等待时间重叠在一起了)。

TCP协议(二)

  • 窗口大小指的是无需等待确认应答而可以继续发送数据的最大值。上图窗口大小就是4000个字节(四个段)

  • 发送前四个段的时候,不需要等待任何ACK,直接可以发送

  • 收到第一个ACK后,滑动窗口向后移动,继续发送第五个段的数据,依次类推

  • 操作系统为了维护这个滑动窗口,需要开辟发送缓冲区来记录当前还有哪些数据没有应答,只有确认应答过的数据才能从缓冲区删掉

  • 窗口越大,则网络的吞吐率就越高
    TCP协议(二)

    注意:
    1.凡是已经发送过的数据,在未收到确认之前都必须暂时保留,以便在超时重传时使用。
    2.发送窗口前沿通常是不断向前移动的,但也有可能不动。这对于两种情况:一是没有收到新的确认,对方通知的窗口大小也不变;二是收到了新的确认但对方通知的窗口缩小了,使得发送窗口前沿正好不动。

    那么,如果出现了丢包,如何进行重传?这里分两种情况讨论。

情况一:数据包已经抵达,ACK被丢失了

TCP协议(二)
这种情况下,部分ACK丢失了并不要紧,因为可以通过后续的ACK进行确认。
情况二:数据包直接丢了:
TCP协议(二)

  • 当某一段报文段丢失之后,发送端会一直收到1001这样的ACK,就像是在提醒发送端“我想要的是1001”一样

  • 如果发送端主机连续三次收到了同样一个“1001”这样的应答,就会将对应的数据1001~2000重新发送

  • 这个时候接收端收到了1001之后,再次返回的ACK就是7001了(因为2000~7001接收端其实之前已经就收到了,被放到了接收端操作系统内核的接收缓冲区中)

    这种机制也被叫做“高速重发控制”(“快重传”)

    根据以上所讨论的,我们还要强调三点:
    第一,虽然A的发送窗口是根据B的接收窗口设置的,但在同一时刻,A的发送窗口并不总是和B的接收窗口一样大。这是因为通过网络传送窗口值需要经历一定的时间滞后(这个时间是不确定的)。另外,发送方A还要根据当时的拥塞情况适当的减小自己的发送窗口数值。
    第二,对于不按序到达的数据应如何处理,TCP标准并无明确的规定。如果接收方把不按序到达的数据一律丢弃,那么接收窗口的管理就会比较简单,但这样做对网络资源的利用不利(因为发送方会重复发送较多的数据)。因此TCP通常对不按序到达的数据是先临时存放在接收窗口中,等到字节流中所缺少的字节收到后,再按序交付给上层的应用进程。
    第三,TCP要求接受方必须有累计确认的功能,这样可以减少传输开销。接收方可以在合适的时候发送确认,也可以在自己有数据发送时把确认信息顺便捎带上。但请注意两点:第一,接收方不应过分推迟发送确认,否则会导致发送方不必要的重传,这反而浪费了网络资源。TCP标准规定,确认推迟的时间不应超过0.5秒。若收到一连串具有最大长度的报文段,则必须每隔一个报文段就要发送一个确认。第二,捎带确认实际并不经常发生,因为大多数应用程序不同时在两个方向上发送数据。