滑动窗口
滑动窗口是什么
上篇提到如果两端发送数据如果是一发一收那就是串行,效率很低,所以可以一次发送多个报文,一次也可以接受多个报文,可以大大的提高性能(其实是将多个段的等待时间重叠在一起了)
那么是怎么发送多个报文的呢?靠的就是滑动窗口这个大杀器
滑动窗口是什么?是为了让tcp并发地发送大量暂时不需要ACK的数据段,从而提高发送效率的解决方案(这里也涉及流量控制的解决方案,和重传机制也有关系!)
如果学习过双指针算法的同学可能听到滑动窗口这个名字很熟悉,其实这里就是类似的
之前我们说过可以把缓冲区当做一个单位大小为char的数组,然后有两个指针win_start和win_end分别指向滑动窗口的起始位置和结束位置
这样发送缓冲区就被分成了三个区域:
滑动窗口的左边区域,处于滑动窗口的区域,滑动窗口的右边区域
分别对应已发送已确认,可以直接发送但是尚未确认,未发送或者没数据的区域
下图为示范例子:
- 窗口大小就是指无需等待确认应答而可以继续发送数据的最大值。图中,窗口大小为4个段,也就是4000个字节。
- 发送前四个段的时候, 不需要等待任何ACK, 直接发送。
- 收到第一个ACK后, 滑动窗口向后移动, 继续发送第五个段的数据; 依次类推。
- 操作系统内核为了维护这个滑动窗口, 需要开辟 发送缓冲区 来记录当前还有哪些数据没有应答; 只有确认应答过的数据, 才能从缓冲区删掉——所以一个数据被发送后,不能立马被移除
- 窗口越大, 则网络的吞吐率就越高
滑动窗口在哪里?
滑动窗口本质上就是发送缓冲区的一个区域
滑动窗口怎么理解?
【win_start,win_end】:滑动窗口!!——本质就是两个数组下标充当指针作用
win_start=确认序号
win_end=win_start(确认序号)+win(接收方的接受能力)——16位窗口大小
所谓的窗口滑动,本质就是下标移动
因为滑动窗口发送的是可以直接发送尚未确认的数据,所以要考虑对方的接受能力,所以滑动窗口的大小由对方的接受能力决定!!!(后续会有变化)
那么最开始的时候,我还没发送数据的时候,滑动窗口的大小是多大?
所以在三次握手的时候,滑动窗口的大小,双方已经协商完毕
滑动窗口的变化以及流量控制
滑动窗口可以往左移动吗?
不可以,win_start=确认序号,而确认序号=序号 +1的,所以滑动窗口只可能往左移动
滑动窗口的大小不变吗?变大?变小?为0?
当确认一个报文后又发送一个报文的时候,滑动窗口的大小就是不变的
当一次发了四五个报文,只有前面两个应答了,那么win_start+=2个报文的长度,滑动窗口也就变小了。后面全部报文都应答了,win_end+=三个报文的长度,滑动窗口也就变大了
所以不变,变大和变小都是可以的——流量控制!!!
接收方能力弱:我就发慢点
接收方能力强:我就发快点
变大变小不变都行,这才叫控制
如果滑动出去,越界了怎么办?把发送缓冲区想象成环形结构即可!!!(环形数组可以用数组来模拟实现)
这里也体现到上篇提到的如果报文发送了,还没应答,就不能丢弃,暂时存起来,存在哪?滑动窗口里
报文丢失
一次发多个数据,那么肯定会有上篇讲的丢失报文的问题,那么报文丢了怎么办?
我们可以将报文丢失分为三类:最左侧丢失,中间丢失,右侧丢失
如果发了序号2000,3000,4000,5000的四个报文,正常情况下发送方应该收到确认序号为2001,3001,4001,5001的ACK
最左侧丢失就是2001ACK没收到,那么此时后面的确认序号就不再是3001,4001,5001了,而是1001,因为确认序号的定义是该序号之前的数据,全部收到
所以连续接收到三个1001ACK,但是明明发的是2000,3000,4000,5000,所以就可以推测出是2000报文丢失了,所以会补发2000报文!!!
如果是中间丢失,也就是3001丢失,那么发送方是接收到2001ACK的,所以窗口会向右移动一个长度,此时中间丢失就变成最左边丢失,重复以上动作即可
如果是最右侧丢失,那么结果还是一样的,窗口不断右移直到丢失的报文成为最左侧,变成最左侧丢失的情况,然后重复上述操作!
总结:不管是什么丢失,最后都会变成最左侧丢失
这里涉及到一个重传机制
快重传:如果连续收到三个同样的确认序号的时候,会立即将对应的报文进行补发!!!
有人会想了,都有快重传了,听名字就挺快的,还要超时重传干什么?
细看的话会发现快重传是有条件的,要连续收到三个同样的确认序号,不是连续的话只能超时重传了
所以超时重传是兜底的,快重传是提高效率的重传策略,两者互相配合
在滑动窗口这里,可以充分体会到确认序号的的价值!!!
还有将缓冲区视为字节环形结构的作用——有了序号和确认序号,保证了可靠性——发送成功或者失败都100%知道!!!
到这里不得不赞叹tcp的设计,几个字段互相配合不仅仅保证了可靠性,还实现了效率的提升
拥塞控制
到目前为止我们讲的都是两个主机之间的,两个主机之间通信还要经过网络这一环节
假设有一天网络突然瘫痪(网络非常繁忙)了,发送的报文迟迟未被接收,那么此时要进行超时重传吗?
如果这时进行超时重传,注意此时不再是两台主机之间通信,可能有很多台主机都向你超时重传发送信息,注意此时网络已经瘫痪,超时重传等于再发一次信息,无异于压死骆驼的最后一根稻草,让网络雪上加霜
所以此时不能进行网络重传
当出现小面积丢包的时候,认为是丢包,进行超时重传
当出现大面积丢包的时候,认为是网络出现问题,不超时重传,而是拥塞控制
什么是拥塞控制呢?就是先试探,慢慢发,随着你应答的次数增多而显著提高发送的数据量
滑动窗口决定了我们单次发送数据量的多少——所以滑动窗口的大小不仅与对方的接受能力有关,还要考虑网络的状态
滑动窗口=min(拥塞窗口大小,对方的窗口大小)
拥塞窗口本质就是一个数字,当发送数据量超过拥塞窗口的时候,有很大的概率会引起网络拥塞
拥塞窗口=2^n,就是个指数函数。可以发现该函数完美切合要求,慢启动,快增长。
前期慢,增长快——前期慢是为了试探网络环境如何,增长快是为了尽快进行正常网络的通信
当拥塞窗口的值超过阈值的时候,拥塞窗口就会变成线性增长,变化规律如图所示,就不赘述
我们发送数据的时候,就是按照图片的规律来发送吗?当然不是,发送数据靠什么?靠滑动窗口?滑动窗口win_start=ack,win_end=min(拥塞窗口大小,对方窗口大小)
所以可能发送到一半就按照对方窗口的大小发送了
但是拥塞窗口必须一直在变化,因为网络的状态要一直探测
tcp的保活机制(心跳机制)
当机器掉电,网线断开的时候,tcp内部有一个计时器,当超过固定时间后仍未应答则自动关闭连接,但是tcp的时间定的太长了,一般更推荐在应用层完成
listen的第二个参数
全连接队列
饭店排队场景类似
如果一个饭店里很多人用餐没有位置了,此时又新来了客人,你直接对客人说走吧没位置了,那肯定会被老板喷个狗血淋头,因为等里面的客人吃完了,如果此时又没新的客人来,高峰期的时候一些座位就会被闲置了
如果你在外面放了一些椅子,让客人在外面等,这时里面的人用完餐了,外面的人就可以直接进去用餐了,高峰期里面的位置被闲置的概率大大降低
但是你也不能在外面摆太长,比如摆个几百张,除了前面几十张等得起,后面的人要等的时间太久了,而且椅子也是占空间的,外面的地可不是都是你们店的
上面的饭店就类似应用层,客人就是连接,外面摆的椅子就是全连接队列,全连接队列不能太长
相关问题
connect参与三次握手吗?
connect参与三次握手,发送SYN请求后,双方操作系统自动完成
accept参与三次握手吗?
当一个服务器通过bind函数开始监听该套接字上的连接请求,一旦有客户端尝试建立连接,三次握手成功后,服务器端的accept调用才会返回,从全连接队列中获得一个新的套接字
所以是不参与的,是发生在三次握手之后的