多点头发,少点代码
本文已经收录至我的GitHub,欢迎大家踊跃star 和 issues。
https://github.com/midou-tech/articles
![](https://tva1.sinaimg.cn/large/00831rSTly1gd3p0nqvh8j30ra0bujs6.jpg)
本来想先更新TCP的基础和TCP可靠性等问题的,但是被你们暗示了,就先更流量控制和拥塞控制了。希望龙叔讲的你能搞清楚,如果有不清楚的,可以加龙叔微信一起探讨。
流量控制
讲流量控制之前先花简短的话语絮叨下TCP基础知识,详细知识细节后面会出文章一一道来。
TCP是一种面向连接、保证可靠性、流式传输服务。面向连接就是建立链接,也就是面试常问的三次挥手建立链接,四次挥手断开链接。保证可靠性到是很好理解,就是你发送的数据尽最大可能保证让接收端接收到。流式传输 就是传输的数据是以字节流的形式发送和接受(不要硬是和我说,什么字节流传输?明明物理层上都是波信号,这,抱拳。)
TCP传输数据都是建立链接之后才进行传输,传输的时候为保证可靠性,也是采用确认应答机制。所谓确认应答机制就是发送数据之后必须收到确认消息,才算一次有效传输。
举个简单栗子,就是你和别人交流之前必须叫别人一声(这位先生你好…. ,这位帅哥你好….,这位同学你好…. 等等),对方收到你的消息,也会回复一声(你好,请问有什么事?),紧接着才会有下面的一堆交流。
试想如果没有这个建立交流的过程,或者对方没有回复你,你的信息是无法可靠的发送出去的。不过在传输协议里还有一种可以不考虑对方是否接受,只管自己消息发送出去即可,那就是UDP传输协议。
了解了TCP的确认应答机制之后方便讲解下面的流量控制,接下来就好好看龙叔给你说流量控制到底是怎么一回事。
流量控制是保证可靠传输的方法之一,所谓流量控制本质就是控制TCP发送数据的速率,不要让发送太快或者太慢让接收方来不解接收或者没东西可收。具体的控制方法或者机制是利用滑动窗口机制实现对TCP传输速率的控制。
这样一说,是不是感觉自己瞬间明白了一些,没有那么迷糊了。别急,下面还有硬核,一定让你明白。
TCP报头里面有两个字节专门表示滑动窗口大小,如下是TCP报头。不用死记硬背,看看就行了。
![](https://tva1.sinaimg.cn/large/00831rSTly1gdaswam5ysj314s0r6gv4.jpg)
看完这个图应该很明确一点,TCP数据段的头部中有2个字节专门表示窗口大小,这也就很明确另外一个问题,每个传输的TCP数据段都有2个字节表示窗口大小,而这2个字节就是流量控制的关键。
不要混淆TCP收发窗口和物理窗口,物理窗口大小是主机所配置的网卡缓冲区大小,一般是固定的,需要通过修改参数配置才能改变。
比如龙叔的网卡缓冲区的发送和接收缓冲大小。
[longshu /home/longshu] 15:04
$ cat /proc/sys/net/core/rmem_max
212992
[longshu /home/longshu] 15:04
$ cat /proc/sys/net/core/wmem_max
212992
TCP接收窗口大小是可变的(根据网络传输情况自适应调整大小)。
来一个滑动窗口的示例。
上面已经给出龙叔的主机物理发送和接收窗口大小都是212992字节,为了讲解方便,先假设如下条件。
- 发送端物理窗口为1W字节
- 每个TCP数据段大小是1000字节
- 用每个数据段的首字段序号表示该数据段的序号
目前发送端收到了接收端发来的一个确认数据段,确认号为3001,窗口大小为5000,因此表示可以连续发送10个数据段,数据段序号从3001~7001。
此时已经达到对方能接受的最大值,如果继续发送数据可能会造成网络拥堵等问题,在没有接收到对方的确认序号和窗口大小之前,我方是不能发送数据的。
等待一会收到对方发来的确认序号为6001,窗口大小为4000,这表示对方已经收到了3001~6001四个数据段,并且可以接收数据段大小为4个,因此我方可以继续发送4个数据段,由于之前发送出去的数据还没完全收到确认,此时只能发送8001、9001、10001这三个数据段,发送之后继续等待收到确认应答号和窗口大小,才能进行下一次发送。
此时收到一个10001序号,窗口大小为6000,就可以连续发送10001~15001这6个数据段。如此往复的过程,就是基本的滑动窗口控制流量的过程。
滑动窗口工作过程如下图。
![](https://tva1.sinaimg.cn/large/00831rSTly1gdawpd4fulj31hs0lytbq.jpg)
过程图解看起来会清晰很多,可以清楚的知道滑动窗口每一次如何变化,以及如何收发。我最开始学习时很难理解滑动这个概念,觉得很抽象。现在看来滑动窗口就是未接受确认应答的数据段。
通过滑动窗口控制流量的方法也被称为 以收订发的原则。
突然想到一个有趣的问题,前段时间我面试过一个粉丝,问到流量控制,对于整个滑动窗口流程答得很流畅,看他答得这么顺畅肯定是准备充分,学习蛮不错的。一想这流量控制就这点内容,就不多问了,准备问个简单的问题就换下一个知识点。
于是我问他 假如对方给我确认应答序号,可接受窗口大小为0,怎么办?
![](https://tva1.sinaimg.cn/large/00831rSTly1gdb9tlkbvmj3073073dfp.jpg)
相信聪明的你一定知道如何回答了,不过龙叔还是絮叨下。
首先对方在确认应答时可以发送窗口大小为0,这点是没问题的。我方在收到对方窗口大小为0时,不是一直等待着,此时我方会启动一个定时器,定时发送一个试探报文给对方,试探报文没有数据的,当对方回复我方窗口大小不为0时继续传输数据;如果为0,重新启动计时器。
这个定时器叫 持续计时器。
拥塞控制
拥塞控制问题也是网络中经常遇到的问题,面试的时候如果遇到拥塞控制问题那你可算有福了,然后你再答对了,那简直就是妥妥滴加分项啊。拥塞控制问题,一般面试官不会问。不要问我为什么,因为问十个同学九个不会,面试官也没啥兴致再问了。
不夸张的讲,TCP中最复杂的问题就是拥塞控制。所以你要是看龙叔的文章,懂了,就是赚了。拿到offer记得给龙叔福报。
网络拥塞就像生活中的城市堵车一样,各方面如此发达的现代社会,交通堵塞问题还是一个头疼的问题。到目前为止还没有一个能完全解决城市堵车问题的方案,目前所有的方法、政策、规则都是减轻、缓解堵塞问题,并没有完全解决。交通堵塞问题的成因多,时效强,多变。网络结构可是比交通结构复杂很多倍
通常我们衡量一个网络的有效处理负荷的能力为 吞吐量,把网络中发送端输入的负荷称之为 输入负荷,理想情况下网络的输入负荷和吞吐量之间的关系是如下图的关系。
![](https://tva1.sinaimg.cn/large/00831rSTly1gdb4ghoa5uj30q20ikjs4.jpg)
理想情况下网络的处理能力随之输入负荷的增大呈线性关系增大,到达网络的最大处理能力时,吞吐量达到最大,此时不会随着输入负荷的增大而增大网络吞吐量。
实际上网络的处理能力是不会呈线性关系的,因为网络不会有理想状况。就像我们的常说的人生一样,理想很丰满,现实很骨感。
![](https://tva1.sinaimg.cn/large/00831rSTly1gdb4r03ygoj30qy0jmwfu.jpg)
看完上面的讲解你不要以为那就简单控制输入负荷就能解决网络拥塞问题,可没那么简单的。
网络拥塞的本质就是 对网络资源的需求大于可用资源 ,可用资源是有限的,因此网络拥塞问题是一直存在的。
从原理上讲,寻找拥塞控制的方案无非就是减少网络资源的需求和增大可用资源,使得两者能够平衡。具体措施有 增大网络的某些可用资源(如业务繁忙时增加一些链路,增大链路的带宽,或使额外的通信量从另外的通路分流),或减少一些用户对某些资源的需求
(如拒绝接受新的建立连接的请求,或要求用户减轻其负荷,这属于降低服务质量)。
但正如上面所讲的,在采用某种措施时,还必须考虑到该措施所带来的其他影响。
实践证明,拥塞控制是很难设计的,因为它是一个动态的(而不是静态的)问题。
当前网络正朝着高速化的方向发展,这很容易出现缓存不够大而造成分组的丢
失。但分组的丢失是网络发生拥塞的征兆而不是原因。
在许多情况下,甚至正是拥塞控制机制本身成为引起网络性能恶化甚至发生死锁的原因。这点应特别引起重视。
虽说网络拥塞机制本身也有可能是恐怖分子,但大多数时候还是解决问题的关键。上面已经说清楚了网络拥塞的原因和本质,下面就来分析下有那些方法可以避免和减少网络拥塞。
目前解决网络拥塞的方法主要有四种,慢开始、拥塞避免、快重传、快恢复。
慢开始(慢启动)
慢启动可不是慢慢或者缓慢启动喔,慢启动指的是在网络建立链接之后不立即发送最大数据段的数据,比如TCP最大数据段是1500字节,慢启动会在建立连接之后第一次发送100个字节,收到确认后发送200字节,再次收到确认就发送300字节,依次增加直到达到最初设定的最大值。
唉,有人会说啦,前面流量控制讲了发送窗口,这里又有一个依次增加发送数据段大小,这么说难道发送窗口控制流量没用了? 别急嘛,龙叔给你说清楚咋回事。
在慢启动方案设计时,大佬们也意识到这个问题。不能发送窗口大小是1000,慢启动增加到1500字节了,这到底依谁的?
慢启动会维持一个拥塞控制窗口叫做 拥塞窗口(CWND)。每次发送出去的字节大小会是两者中的较小值。举个例子,发送窗口可以发送1500字节,但是此时拥塞窗口是1000字节,最终发送出去的字节就是1000字节。
拥塞窗口大小也是变化的。TCP建立完成后会有一个初始化的拥塞窗口大小。初始化拥塞窗口大小为当前TCP链接使用的最大数据段大小(Maximum Segment Size,MSS)
当发送一次数据后,在定时器过期之前收到了确认应答消息(ACK),则拥塞窗口大小变为原来的2倍,依次往复,只要在定时器过期之前能收到ACK,都会增大2倍。一直到数据分组发生丢失,这个发生丢分组的点叫做 慢启动阈值(Slow Start Threshold,SSTHRESH),初始化慢启动阈值是64KB。
到达阈值后拥塞窗口大小衰减为最初的大小即1MSSS大小,慢启动阈值衰减为原来阈值的一半。此后又是一个新的慢启动过程。不过拥塞窗口(CWND)再次到达慢启动阈值(SSTHRESH)之后,会启动拥塞避免机制。
拥塞避免
拥塞避免是在慢启动的基础上的,当慢启动的拥塞窗口第二次达到阈值的时候启动拥塞避免,这也说明都第二次时网络必然是有些问题存在了。
拥塞避免就是控制拥塞窗口增长速度。之前慢启动是拥塞窗口是以当前窗口大小的二倍速度增长的,现在网络有问题啦,不能再以这种速度增大拥塞窗口了,不然一直这样继续估计最后网络可传输的字节就是,n趋近于无限大,可传输字节无限趋近于0。这就没法玩了,就是死锁了。
于是拥塞避免在第二次慢启动到达阈值后就使拥塞窗口呈线性增长而不是指数增长。弄个图看下具体过程
![](https://tva1.sinaimg.cn/large/00831rSTly1gdb943im5yj31ba0o211i.jpg)
快重传
快重传就是一个字快,在一个TCP数据段发送出去之后会启动一个定时器,看是否接受ACK会超时。然而快重传就是解决这个只能通过定时器超时才能判断数据是否丢失的机制,达到快速判断的效果,快速判断才能早点发送已经丢失的数据。节省时间,减少网络阻塞。
什么情况下才会触发快重传机制呢?可不是随便一个数据段过来,就直接触发快重传了。
当接收端接收到不是按序到达的数据段时,此时接收端立即发送一个ACK响应报文,接收端接到三次重复ACK报文即可确认该数据段丢失,此时清零计时器,触发重传数据。
这一整个流程就是所谓的快重传。快,没话说吧。比你超时重传快,根本不用等计时器超时。整个过程如图所示。
![](https://tva1.sinaimg.cn/large/00831rSTly1gdb9k10z28j30ry0sw45r.jpg)
快恢复
快恢复是在快重传的基础上的。当快重传已经发送了丢失的数据后,快恢复机制被触发。
在收到第三个重复ACK时,把当前CWND值设为当前SSTHRESH值的一半,以减轻网络负荷,然后执行前面介绍的“拥塞避免”算法,使CWND值慢慢增大,以避免再次出现网络拥塞。
综上我基本说完了,拥塞控制的全部内容,如果有不明白的地方可以私信龙叔一起学习。
我是龙叔,一个分享互联网技术和心路历程的大叔。支持我,记得给我点赞和分享。