一、续
当窗口大小为0,意味着缓冲区满了,此时发送方,就因该暂停发送,发送方会周期性的除法 " 窗口探测包 " ,并不携带载荷,这样的包对于业务不产生影响,只是为了触发ACK,一旦查询出来的结果是非0,缓冲区右有空间了,发送方就可以继续发送.
二、拥塞控制
要限制发送方发送数据的速率
如果当前接收方处理数据的速度非常快,但是中间路径出现了问题,发送速度再快也没有用.
木桶效用 : 能装多杀水,取决于最短的板
针对这种情况,核心思路,把中间的路径经过的所有设备看作一个整体,通过实验的方式找到最快的方式.
如果按照某个窗口大小发送数据之后,出现丢包,就视为中间路径存在拥堵,就减小窗口大小,
没出现丢包,视为中间路径不存才拥堵,就增大窗口大小.
这种方案,一方面简化了问题,另一方面也能很好地适应,当前网络环境的复杂性.
中间这些节点,什么时候出现拥堵,什么时候不拥堵,都是随机的
此时按照以上策略,就可以让发送速率,.动态变化.
总的原则是,流量控制和拥塞控制,谁产生的窗口大小更小,谁说的算.
拥塞控制具体是怎么把窗口大小给试出来的?
1. 慢启动.刚开始传输的数据,速率是比较小的,采用的窗口 (拥塞窗口) 大小也就比较小.此时,网络的拥堵情况未知,如果上来就很大,可能会出问题.
2. 此时上述传输的数据,没有出现丢包,说明网络还是通畅的,就要增大窗口大小,此时,增大的方式是按照指数来增长的.
由于慢启动,.开始的时候,窗口大小非常小,也有可能网络上就是很畅通,通过指数可以让上述窗口快速增长,这样就可以保证效率了.
3. 指数增长,不会一直保持的,可能会增长太快,一下子导致网络拥堵.
这里引入了一个 "阈值",当拥塞窗口达到阈值后,此时指数增长就变成线性的了.
(线性增长能使当下窗口保持在一个很快的效率,并且不容易丢包.
4. 线性增长也是一直在增长,积累一段时间之后,传输速度可能太快,此时还是会引起丢包,一旦出现丢包,就把拥塞窗口重置成最小的值,回到最初的慢启动的过程(再次指数增长),并且这里也会根据刚才丢包的窗口大小,重新设置指数增长到线性增长的阈值.
三、延时应答
也是基于滑动窗口,是要尽可能在提高一点效率.
结合滑动窗口及流量控制,能够通过延时应答ACK的方式,把反馈窗口的大小变得更大一点.
核心就在于在允许范围内,使,窗口尽可能的变大.
接收方收到数据之后,不会立即返回ACK,而是稍等一下,等一下再返回ACK.
等了一会儿,相当于给接收方的应用程序这里,腾出来更多的时间,来消费这里的数据.
也就是等一会儿,让接受让先处理一下数据,就会让缓冲区空余空间变大,然后再发送更多的数据.
延时应答是按照ACK丢了的方式来处理,因为滑动窗口中ACK丢了也不会产生什么影响.
正常每个数据都有ACK,此时就可以隔几个数据返回一次ACK,
四、捎带应答
基于延时应答,引入的机制,能够提升传输效率.
修改窗口大小,确实是提高效率的有效途径.
捎带应答,就是走另一条路,尽可能的把能合并的数据包进行合并,从而起到提高效率的效果.
正常情况下,服务器返回的ACK和response之间,有一定的时间间隔,此时就得分两个包发送了.
由于有了延时应答, 服务器收到请求后,要执行业务逻辑,根据请求计算响应.
ACK延时这段时间里,响应数据刚好准备好了,此时就可以把AKC和响应数据合并成一个TCP数据报
本身ACK也不带载荷,只是把报头中的ACK标志位设为1,并且设置确认序号以及窗口.
这几个属性不会冲突.
五、面向字节流
"粘包问题"
此处的包是 " TCP载荷中的应用数据包"
TCP传输的数据到了接收方后,接收方要根据socket api 来 read 出来,read出来的结果就是应用层数据包.
由于整个read过程非常灵活,可能会使代码中无法区分出当前的数据是从哪到哪是一个完整的应用数据包,
如上图,就无法确定从哪到哪是一个数据.
解决问题的关键,就是"明确包与包之间的边界"
1. 通过特殊符号,作为分隔符,见到分隔符,就视为一个包结束了
2. 指定出包的长度,比如在包开始的位置,加上一个特殊的空间来表示整个数据的长度.
上述这样的问题,都应该是在设计应用层协议的时候就设计好了的.
六、异常问题
考虑比丢包更严重的情况,甚至说网络直接出现故障等情况
1. 其中有一方出现了进程崩溃
进程无论是正常结束,还是异常崩溃,都会触发到回收文件资源,关闭文件这样的效果(系统自动完成的),就会触发四次挥手
TCP的生命周期.可以比进程更长一点,虽然进程已经退出了,但是TCP连接还在,仍然可以继续进行四次挥手.
2. 其中有一方关机了(正常流程)
关机会强制终止所有进程,此时四次挥手不一定能完成
如果够快,此时,本段和对端都能正确删除保存的连接信息.
如果不够快,至少也把第一个FIN发给对方,告诉对方我这里要结束了.
对端收到FIN后,对端也要进入释放连接的流程了,返回ACK,并且也发FIN,这里发的FIN不会再有ACK了.
FIN没有收到ACK之后,会进行重传,达到指定次数后,就单方面释放连接了.
3. 其中一方出现断电.
3.1 断电的是接收方,发送方会突然发现没有ACK了,就要重传.
重试几次后还是不行,TCP就会尝试复位连接.(相当于清楚原来的TCP中的各种临时数据,重新开始.)
这就需要用到TCP中的一个复位报文段.
通过此处的RST,报文直接复位.
3.2 断电的是发送方
接收方本来在阻塞等待发送方的消息,结果一直没来.
这是就要区分,发送方是挂了还是没准备好.
TCP中,接收方一段时间后,没有收到消息,就会触发 " 心跳包 " 来询问对方的情况.
心跳包:不携带应用层数据包的特殊数据包,1. 周期的 2. 没有心跳,视为是对端挂了.
如果对端没有心跳了,此时本端也会尝试复位并且单方面释放连接了.
4. 网线断开.
这种情况本质上就是3的3.1 和 3.2 的结合了.