🌈个人主页 :努力学编程'
⛅个人推荐 :
c语言从初阶到进阶
JavaEE详解
数据结构
⚡学好数据结构,刷题刻不容缓 :点击一起刷题
🌙心灵鸡汤 :总有人要赢,为什么不能是我呢
🌏🌏🌏TCP核心机制四-(滑动窗口)
这里的滑动窗口可能有很多人之前已经听过了,我们在做法算题的时候确实会有用到滑动窗口的具体场景,其实这里借鉴的就是TCP中的滑动窗口,我们之前说过TCP的传输是稳定传输,但是它为此牺牲掉了传输数据的效率,为了提高效率,我们引入滑动窗口,提高数据的传输效率!!!
需要注意,这里提高数据的传输效率其实是 "亡羊补牢" 还是在保证数据安全传输的前提下,提高传输效率的,所以他的传输效率还是远远不如UDP的
滑动窗口的基本原理:我们在上篇文章中提到了,客户端和服务器之间的交互关系,每次发送一个数据,就等待响应,发送一个,等待一个... 这里的滑动窗口就是将数据集中起来,发送一批,等待一批...
每当我们发送一批数据之后,第一个数据的ack接收以后,我们是继续进行后面的操作呢,还是等待后面的数据的ack呢, 不会等待后序的ack此时滑动窗口就会自动向后移动,继续处理后面的数据,这样就会非常高效的处理数据.
之前的交互场景:
引入滑动窗口后的交互场景:
我们引入滑动窗口的前提是 "稳定传输" 那么滑动窗口中是如何处理丢包这样的问题呢?
情况一: ack的丢失
我们刚才说到 发送一批数据,等待一批数据 正式由于这样的机制其实我们就可以不必对丢失的ack进行处理,如果我们的数据包一的ack丢失了,但是只要后序的ack传到主机A这里,就会默认前面的所有的数据包都已经传输成功了.
总之,你的ack总不会全部丢失,只要有后序的ack传输过来,我们就会确认之前的数据包传输的成功,这样就解决了ack丢失的情况.
情况二: 数据包直接丢失了
举个例子,数据包二(1001-2000)丢失了,当我们的第一个数据包传输成功之后,主机B就会等待1001开始的数据,此时如果主机A传输的是数据包三(2001-3000),那么主机B此时并不会处理数据三中的数据,而是持续向主机A索要数据包二的信息(即进行多次的重复应答),当主机A收到了三次重复的确认应答的时候,就会对数据进行重传.从而处理数据包丢失的情况.
这个过程可以给大家模拟一下同样的场景:
有一个集美要参加闺蜜的婚礼,去骑车的时候突然发现车子开不了,于是打电话给男朋友:
集美: 怎么办,车子打不开呀~
男友:那看看是不是车子没电了.
集美: 哎呀,快迟到了真是着急~
男友:那看看是不是车子没电了.
集美:这可是我最好的闺蜜呀~
男友:那看看是不是车子没电了.
集美:这可怎么办呀~
男友:我tm不是让你看看是不是车子没电了吗~
集美: 你竟然凶我 呜呜呜~~ 哎呀车子好像就是没电了...
这里需要强调的是,尽管我们主机B'一直对主机A索要数据包二,但是在主机A给主机B传输其他数据的时候,主机B也会对这些数据进行处理,我们处理数据包二(丢失的包)之后,前面传输的包也都被数据包B接收到了~~
这整个过程我们称为 "快速重传" .这里可以对比记忆两个场景: 确认应答 , 超时重传
滑动窗口,快速重传~
🌏🌏🌏TCP核心机制五-(流量控制)
我们上面所说的滑动窗口可以提高数据传输的效率,那么窗口的大小可以无限地大吗?是如何确定滑动窗口的大小呢,在两台主机传输大量数据的时候,会触发滑动窗口机制,主机A对主机B进行数据传输的时候,主机B的内核中的内存中有一个内存缓冲区,这就是主机B的接收数据的地方,本质上是一个阻塞队列,当我们传输的数据远远大于主机B的内存缓冲区时,就需要主机B的场景能被主机A感知到,从而控制主机A的数据传输.这就是流量控制.
这里给大家举个例子:
我们可以形象的将传输数据的这个过程看做一个 "水池" 流量控制,我们通过实时的这个水池的空闲空间来实现流量控制.
当空闲空间比较大的时候:
此时我们认为应用程序的处理能力比较强,传输的数据可以被很快消化,这时我们就可以加快数据的传输.
当空闲空间比较小的时候:
此时我们认为程序的处理能力比较弱,数据没有被及时消化,此时我们就要控制数据传输的速度了~
这样两个机制就配合完成了TCP核心机制-(流量控制).
TCP中,接收方在接收到数据的时候,就会把缓冲区的剩余空间大小通过 ACK 数据报 反馈给发送方.
接着,发送方就可以依据这个数据来设置发送的窗口大小了~
这里给大家演示一下关于流量控制的具体过程:
ps:流量控制并非是TCP独有的机制,在数据链路层中类似的协议也有流量控制的机制.
🌏🌏🌏TCP核心机制六-(拥塞控制)
流量控制: 站在接收方的视角来限制发送方的速度~
拥塞控制: 站在传输链路的视角来限制发送方的速度的~
这里如果A给B传输大量数据的时候,就可能使数据链路被破坏掉~
拥塞控制中,我们是不关心数据链路中具体实现细节的,将他视为一个整体
我们可以发现数据链路中有很多的中间节点,如果我们关心其中的细节,就麻烦了.
即使现在这个问题变得非常麻烦,但是办法总比困难多,程序员大佬们还是解决了这个问题,简单来说就是 "面多 加水, 水多 加面".
我们现以一个比较小的速度,发送数据
如果数据非常畅通,没有丢包,说明网络中的数据传输是比较同场的,此时我们就可以加快数据传输的速度.
增大到一定的速度之后,发现出现丢包了,说明网络上存在拥堵了,就慢慢降低传输数据的速度
减速之后,如果数据可以正常传输了,我们就加快数据传输的速度,
加速之后又丢包,又减速~~
即这个过程是持续变化的
流量控制,会限制发送窗口
流量限制,也会限制发送窗口
这两个机制会同时起作用,来共同限制数据的发送
拥塞控制的流程图:
大致流程如下:
-
刚开始传输数据的时候,拥塞窗口非常小,用一个很小的速度来发送数据
由于此时网络是否阻塞我们是未知的,所以此时传输的速度会非常慢. -
不丢包,增大窗口的大小,指数增大
增长的熟读非常快,在短时间内达到很大的窗口大小 -
增长到一定程度,达到某个指定的阈值,即使此时没有丢包,也会停止指数的增删,变成线性增长
不至于很快进入丢包操作的过程中 -
线性增长,也会持续使发送的速度越来越快,达到某个情况之下,就会出现丢包~~
一旦出现丢包操作,就要减小发送的速度,减下窗口的大小 -
接下来就有两种处理方式:
回归到开始非常慢的初始值,指数增长,线性增长
现在的方案,回归到新的阈值,线性增长(以后都不会线性增长~)
这里我们所说的流量控制和拥塞控制的作用其实都是对TCP "可靠传输" 的补充.