TCP为什么可靠?

共 3754字,需浏览 8分钟

 ·

2021-06-02 13:18


目录

  • 前言

  • 实现一个可靠的协议?

  • 滑动窗口

    • 顺序、丢包问题

    • 确认和重发机制

    • 流量控制

    • 拥塞控制

前言

上一篇记录了TCP三次握手四次挥手的细节以及为什么会在TIME_WAIT状态停留时间为2MSL。

一直说TCP是可靠的协议,那么它靠什么成为一个可靠的传输协议?

实现一个可靠的协议?

顺序性: 每个包都有一个序号,在建立连接时,双方确定起始序号,然后按需发送

不丢包: 对于发送的包都要进行应答,但不是发一个应答一个,而是应答某一个,而这个序号之前的包,表示都收到了,这种模式成为累计确认或者累计应答

滑动窗口

TCP使用的被称为滑动窗口协议的另一种形式的流量控制方法,该协议允许发送方在停止并等待确认前可以连续发送多个分组。

由于发送方不必每发一个分组就停下来等待确认,因此该协议可以加速数据的传输。

为记录所有发送和接收的包,TCP需要发送端和接收端分别都有缓存来保存这些记录

发送端的缓存里时按照包的序号一个个排列,按照处理情况分为四个部分:

  • (1)发送并已确认
  • (2)发送但未确认
  • (3)没发送但等发送
  • (4)没发送但暂不发送

滑动窗口的大小是由接收端提供的,也即是上述 【 第二点 + 第三点 】,超过这个大小,接收端处理不过来

对接收端来说,缓存里记录的内容如下:

  • (1)接收并已确认
  • (2)已接收但未确认
  • (3)等待接收的
  • (4)不能接收的,超过窗口大小

书中例子:

数据传输过程中滑动窗口的变化

顺序、丢包问题

发送端:

接收端:

分析如下:

(1)包1、2、3双方都达成了一致

(2)包4、5接收方确认了,但是发送方还没收到接收方的确认,有可能丢了或者在路上

(3)包6、7、8、9已经发送,但是包8、9已经到接收端,包6、7没到,此时会出现乱序,所以包8、9只能放在缓存中,不能进行ACK。

确认和重发机制

超时重传

对每一个发送了但是没有确认的包,都设定一个定时器,超过定时,就重新尝试。

超时时间不宜过短,必须大于往返时间RTT,否则会有不必要的重传

估计往返时间,需要TCP通过采样RTT的时间,然后进行加权平均,算出一个值,而且这个值是不断变化的,因为网络状况在不断的变化,除了采用RTT,还要采用RTT的波动范围,计算出一个估计的超时时间。由于重传时间是不断变化的,称为自适应重传算法。

TCP的策略是超时间隔加倍,每当遇到依次超时重传的时候,都会将下一次超时时间间隔设为先前值的两倍,两次超时,说明网络环境差,不宜频繁反复发送。

快速重传:

当接收方收到一个序号大于下一个所期望的报文段时,就会检测到数据流中的一个间隔,于是它就会发送冗余的 ACK,冗余 ACK 的是期望接收的报文段。而当客户端收到三个冗余的 ACK 后,就会在定时器过期之前,重传丢失的报文段。

例如:接收方发现 6 收到了,8 也收到了,但是 7 还没来,那肯定是丢了,于是发送 6 的 ACK,要求下一个是 7。接下来,收到后续的包,仍然发送 6 的 ACK,要求下一个是 7。当发送端收到 3 个重复 ACK,就会发现 7 的确丢了,不等超时,马上重发。

流量控制

在对包的确认中,同时会携带一个窗口的大小

发送端发送的每一个数据包,服务端都要给一个确认包(ACK),确认它收到了。

服务端给发送端发送的确认包(ACK包)中,同时会携带一个窗口的大小,

此窗口大小就代表目前服务器端的处理能力(接收端最大缓存量 - 接收端已确认但未被应用层读取的部分)

此窗口大小也是时刻变化的,可能接收方在发送数据包4的ACK时候,窗口大小为9,此时应用层程序已经读取确认了缓存4个数据的,接收方再发送数据包5的ACK的时候,窗口的大小就变为14

例如:窗口不变,始终为9

初始情况:

注意:点点框起来的是窗口

发送端:

当收到ACK4时,窗口右移,此时第13个包也可以发送

此时发送端发送较快,将包10、11、12、13 全部发送,此时未发送可发送部分为0

当收到ACK5时,窗口向右滑动一格,此时14可发送

如果接收方处理速度比不上发送方发送速度,导致缓存中没有空间,可通过确认信息修改窗口大小,甚至可设置为0,则发送方将暂时停止发送

假设接收方应用一直不读取缓存中数据,当ACK6到达发送方后,此时携带的窗口大小变成了8

此时发送方仅仅是将左边进行右移,窗口大小从9改成了8

接收端应用一直不读取数据,那么就会一直发送ACK给发送端,直到窗口大小为0

当ACK14到达时,窗口为0,停止发送

此时发送方会定时发送窗口探测数据包,看是否有机会调整窗口大小。

拥塞控制

拥塞控制也是通过窗口的大小控制的,流量控制的滑动窗口rwnd是怕发送方把接收方缓存塞满,而拥塞窗口cwnd,是怕把网络塞满

  • rwnd:receiver window,接收方滑动窗口,用于防止接收方缓存占满
  • cwnd:congestion window,拥塞窗口,用于控制将带宽占满

白话就是发送端先发个最小的包,等确认返回了,发送端再慢慢增加发送的包的大小,直到达到设定的最大的包大小,此时发送端增加速率是1/(包大小)

比如第一次发大小为1,返回确认之后,就可以发2,等发送的两个都返回之后,就发送4,直到最大的65535,此时返回确认之后,发送的包增加1/65535大小,这样就不会突然导致出现网络卡顿的情况。

发送方怎么判断网络是不是慢?

对于TCP协议来说,他不知道整个网络路径都会经历什么,TCP发送包常被比喻为往一个水管里面灌水,而TCP的拥塞控制就是在不堵塞、不丢包的情况下,尽量发挥带宽。

网络有带宽,端到端有时延

通道的容量= 带宽 * 往返延迟

如果设置发送窗口,使得发送但未确认的包为通道的容量,就能撑满整个管道。

假设往返时间为8s,去4s,回4s,每秒发送一个包,每个包1024byte。

8s后8个包全发出去了,其中前4个包已经到达接收端,但是ACK还没返回,不算发送成功,5-8个包还在路上没被接收,此时整个管道正好撑满。

在发送端,已发送未确认的有8个包,正好等于带宽,也即是每秒发送一个包,乘于往返时间8s。

如果在此基础上调大窗口,单位时间可发送更多的包,那么单位时间内,会有更多的包到达中间设备,但设备在单位时间的处理速度跟不上,多出来的包就会被丢弃。

此时,可以在中间设备上添加缓存,此时包不会丢失,但是会增加时延,如果时延到达一定成都,会进行超时重传。

所以TCP的拥塞控制主要避免两种现象:包丢失和超时重传。

出现这两个现象就说明发送速度过快,需要慢一点。

如果发送速度从慢开始,发现可以加快速度的发,这叫慢启动。类似于车的加速。

一条TCP连接开始,cwnd设置为1个报文段,一次只能发1个,当收到这一个确认时候,cwnd加1,此时可以一次发2个,当这两个确认到来时,每确认一个,cwnd加1,此时cwnd为4,可以发4个。这是指数型增长。

直到cwnd达到ssthresh=65535个字节的时候,就需要慢下来。

此时每收到一个确认后,cwnd增加1/cwnd,一次发送8个,8个确认一共是8*(1/8)=1,于是此时可以发送9个,变成了线性增长。

拥塞的一种表现形式是丢包,需要超时重传,此时,将sshresh设为cwnd/2,将cwnd设为1,重新开始慢启动,此时传输速度就会立马慢下来,造成网络卡顿。

快速重传算法。当接收端发现丢了一个中间包的时候,发送三次前一个包的 ACK,于是发送端就会快速地重传,不必等待超时再重传。TCP 认为这种情况不严重,因为大部分没丢,只丢了一小部分,cwnd 减半为 cwnd/2,然后 sshthresh = cwnd,当三个包返回的时候,cwnd = sshthresh + 3,也就是没有一夜回到解放前,而是还在比较高的值,呈线性增长。

例如,此时cwnd为6,有一个包迟迟没返回ACK,此时cwnd变成3,ssthresh=cwnd,当收到三个请求发送这个包的ACK时,cwnd = ssthresh + 3 = 6

这样就不会一下导致网络卡顿。

TCP 的拥塞控制主要来避免的两个现象都是有问题的

  • 第一个问题是丢包并不代表着通道满了,也可能是管子本来就漏水。例如公网上带宽不满也会丢包,这个时候就认为拥塞了,退缩了,其实是不对的。
  • 第二个问题是 TCP 的拥塞控制要等到将中间设备都填充满了,才发生丢包,从而降低速度,这时候已经晚了。其实 TCP 只要填满管道就可以了,不应该接着填,直到连缓存也填满。

为了优化这两个问题,后来有了 TCP BBR 拥塞算法。它企图找到一个平衡点,就是通过不断地加快发送速度,将管道填满,但是不要填满中间设备的缓存,因为这样时延会增加,在这个平衡点可以很好的达到高带宽和低时延的平衡。




往期推荐:

详解tcp三次握手四次挥手

DNS域名解析系统

广播与多播

UDP是什么东西???

路由协议

网关和路由

CV工程师用不到的链路层知识点

星巴克气氛组电脑是如何获取IP的

浏览器发起HTTP请求后经历了什么?

推荐一个生产环境问题排查利器

MySQL系列文章

k8s全系列文章汇总


浏览 130
点赞
评论
收藏
分享

手机扫一扫分享

分享
举报
评论
图片
表情
推荐
点赞
评论
收藏
分享

手机扫一扫分享

分享
举报