有关 TCP 你不能不知道的三次握手和四次挥手问题,点我跳转
流量控制
1. 滑动窗口
数据的传送过程中很可能出现接收方来不及接收的情况,这时就需要对发送方进行控制以免数据丢失。利用滑动窗口机制可以很方便地在 TCP 连接上对发送方的流量进行控制。TCP 的窗口单位是字节,不是报文段,发送方的发送窗口不能超过接收方给出的接收窗口的数值。
TCP 规定,即使设置为零窗口,也必须接收以下几种报文段:
- 零窗口探测报文段
- 确认报文段
- 携带紧急数据的报文段
确认丢失和确认迟到
持续计时器
存在这样一种情况:发送方接收到零窗口报文之后将发送窗口设置为 0,停止发送数据。但等到接收方有足够缓存,发送了非零窗口大小的报文,但是这个报文中途丢失,那么发送方的发送窗口就一直为 0 导致死锁。
为此,TCP 为每一个连接设有一个持续计时器(Persistence Timer):当 TCP 连接的一方收到对方的零窗口通知时就启动持续计时器。若持续计时器时间到期,就发送一个零窗口探测报文段(携有 1 字节的数据),那么收到这个报文段的一方就在确认这个探测报文段时给出了现在的窗口值。若窗口仍然是零,则收到这个报文段的一方就重新设置持续计时器;若窗口不是零,则死锁的僵局就可以打破了。
2. 延迟 ACK
如果 TCP 对每个数据包都发送一个 ACK 确认,那么只是一个单独的数据包为了发送一个 ACK 代价比较高,所以 TCP 会延迟一段时间,如果这段时间内有数据发送到对端,则捎带发送 ACK,如果在延迟 ACK 定时器触发时候,发现 ACK 尚未发送,则立即单独发送;
延迟 ACK 好处:
- 避免糊涂窗口综合症。
- 发送数据的时候将 ACK 捎带发送,不必单独发送 ACK。如果延迟时间内有多个数据段到达,那么允许协议栈发送一个 ACK 确认多个报文段。减少流量消耗。
糊涂窗口综合症:TCP 接收方的缓存已满,而交互式的应用进程一次只从接收缓存中读取 1 字节(这样就使接收缓存空间仅腾出 1 字节),然后向发送方发送确认,并把窗口设置为 1 个字节(但发送的数据报为 40 字节的的话)。当发送方又发来 1 个字节的数据(发送方的 IP 数据报是 41 字节),接收方发回确认,仍然将窗口设置为 1 个字节。这样,网络的效率很低。要解决这个问题,可让接收方等待一段时间,使得或者接收缓存已有足够空间容纳一个最长的报文段或者等到接收方缓存已有一半的空闲空间。只要出现这两种情况,接收方就发回确认报文,并向发送方通知当前的窗口大小。此外,发送方也不要发送太小的报文段,而是把数据报积累成足够大的报文段,或达到接收方缓存的空间的一半大小。
拥塞控制
拥塞控制与流量控制的区别 :
拥塞控制是防止过多的数据注入到网络中,可以使网络中的路由器或链路不致过载,是一个全局性的过程。
流量控制是点对点通信量的控制,是一个端到端的问题,主要就是抑制发送端发送数据的速率,以便接收端来得及接收。
拥塞控制的作用
拥塞控制是为了防止过多的数据注入到网络中,这样可以使网络中的路由器或者链路不至于过载。
拥塞控制的算法
我们假定:
- 数据单方向传送,而另外一个方向只传送确认。
- 接收方总是有足够大的缓存空间,因为发送窗口的大小由网络的拥塞程度来决定。
发送方的发送窗口的上限值应当取为接收方窗口 rwnd 和拥塞窗口 cwnd 这两个变量中较小的一个,即发送窗口的上限值为 Min[rwnd, cwnd]
当 rwnd < cwnd 时,是接收方的接收能力限制发送窗口的最大值
当 cwnd < rwnd 时,则是网络的拥塞限制发送窗口的最大值
拥塞控制的过程一共涉及了 4 种算法:
- 慢启动
- 拥塞避免
- 快重传
- 快恢复
1. 慢启动
发送方维护一个拥塞窗口 cwnd 的状态变量,拥塞窗口的大小取决于网络的拥塞程度,动态变化。通过逐渐增加 cwnd 的大小来探测可用的网络容量,防止连接开始时采用不合适的发送量导致网络拥塞。
当主机开始发送数据时,如果通过较大的发送窗口立即将全部数据字节都注入到网络中,由于不清楚网络状况,有可能引起网络拥塞。较好的方法是试探,从小到大逐渐增大发送端拥塞窗口的 cwnd 数值。
例如:开始发送方先设置 cwnd=1,发送第一个报文段 M1,接收方接收到 M1 后,ACK 返回给发送端,发送端将 cwnd 增加到 2,接着发送方发送 M2,再次接受到 ACK 后将 cwnd 增加到 4 … 慢启动算法每经过一个传输轮次,拥塞窗口 cwnd 就加倍。
当 rwnd 足够大时,为防止拥塞窗口 cwind 的增长引起网络拥塞,还需要另外一个变量,慢开始门限 ssthresh
当 cwnd当 cwnd=ssthresh,既可使用慢开始算法,也可以使用拥塞避免算法
当 cwnd>ssthresh,使用拥塞避免算法
首次慢启动的 ssthresh 值,可以参阅网上的各种讨论,限于篇幅,本文不作介绍~
2. 拥塞避免
控制过程:
- TCP 连接初始化,将拥塞窗口 cwnd 设置为 1 个报文段,即 cwnd=1
- 执行慢开始算法,cwnd 按指数规律增长,直到 cwnd == ssthresh 时,开始拥塞避免算法,cwnd 按线性规律增长
- 当网络发生阻塞,把 ssthresh 值更新为拥塞前 cwnd 的一半 (12=24/2),cwnd 重新设置为 1,再按照(2) 执行
让拥塞窗口 cwnd 缓慢地增大,每经过一个往返时间 RTT 就把发送方的拥塞窗口 cwnd+1,而不是加倍。这样拥塞窗口 cwnd 线性缓慢增长,比慢开始算法的拥塞窗口增长速率缓慢地多。
无论慢启动开始阶段还是在拥塞避免阶段,只要发送方判断网络出现拥塞(没收到 ACK),就把慢启动门限 ssthresh 设置为出现拥塞时的 cwnd 的一半。然后把拥塞窗口 cwnd 重新设置为 1,执行慢启动算法。这样做的目的是能迅速的减少主机向网络中传输数据,使发生拥塞的路由器能够把队列中堆积的分组处理完毕。拥塞窗口是按照线性的规律增长,比慢启动算法拥塞窗口增长快的多。
拥塞避免是由指数增长拉低到线性增长,降低出现拥塞的可能,并不是能完全避免网络拥塞
3. 快重传
一条 TCP 连接有时会因等待重传计时器的超时而空闲较长的时间,慢开始和拥塞避免无法很好地解决这类问题,因此提出了快重传和快恢复的拥塞控制方法。
为使发送方及早知道有报文没有达到对方,快重传算法首先要求接受方每收到一个报文段后就立即发出重复确认。快重传算法并非取消了重传机制,只是在某些情况下更早地重传丢失的报文段。即,当 TCP 源端收到 3 个相同的 ACK 确认时,即认为有数据包丢失,则源端重传丢失的数据包,而不必等待 RTO(Retransmission Timeout)超时。由于发送方尽早重传未被确认的报文段。因此,采用快重传后可以使整个网络吞吐量提高 20%
快重传算法要求首先接收方收到一个失序的报文段后就立刻发出重复确认,而不要等待自己发送数据时才进行捎带确认。接收方成功的接受了发送方发送来的 M1、M2 并且分别给发送了 ACK,现在接收方没有收到 M3,而接收到了 M4,显然接收方不能确认 M4,因为 M4 是失序的报文段。如果根据可靠性传输原理接收方什么都不做,但是按照快速重传算法,在收到 M4、M5 等报文段的时候,不断重复的向发送方发送 M2 的 ACK, 如果接收方一连收到三个重复的 ACK, 那么发送方不必等待重传计时器到期,由于发送方尽早重传未被确认的报文段。
4. 快恢复
快恢复算法控制过程:
当发送方连续收到 3 个重复确认时,发送方认为网络很可能没有发生拥塞,因此不执行慢启动。而是把 cwnd 值设为新的门限值,然后执行拥塞避免算法,cwnd 值线性增大,避免了当网络拥塞不够严重时采用 ” 慢启动 ” 算法而造成过大地减小发送窗口尺寸的现象,这就是快恢复。
最后,限于笔者经验水平有限,欢迎读者就文中的观点提出宝贵的建议和意见。如果想获得更多的学习资源或者想和更多的是技术爱好者一起交流,可以关注我的公众号『全菜工程师小辉』后台回复关键词领取学习资料、进入前后端技术交流群和程序员副业群。同时也可以加入程序员副业群 Q 群:735764906 一起交流。