TCP 协议灵魂问题,巩固你的网路底层基础( 六 )


也就是说 , 以前一个RTT下来 , cwnd翻倍 , 现在cwnd只是增加1而已 。
当然 , 慢启动和拥塞避免是一起作用的 , 是一体的 。
快速重传和快速恢复
快速重传
在TCP传输的过程中 , 如果发生了丢包 , 即接收端发现数据段不是按序到达的时候 , 接收端的处理是重复发送之前的ACK 。
比如第5个包丢了 , 即使第6、7个包到达的接收端 , 接收端也一律返回第4个包的ACK 。 当发送端收到3个重复的ACK时 , 意识到丢包了 , 于是马上进行重传 , 不用等到一个RTO的时间到了才重传 。
这就是快速重传 , 它解决的是是否需要重传的问题 。
选择性重传
那你可能会问了 , 既然要重传 , 那么只重传第5个包还是第5、6、7个包都重传呢?
当然第6、7个都已经到达了 , TCP的设计者也不傻 , 已经传过去干嘛还要传?干脆记录一下哪些包到了 , 哪些没到 , 针对性地重传 。
在收到发送端的报文后 , 接收端回复一个ACK报文 , 那么在这个报文首部的可选项中 , 就可以加上SACK这个属性 , 通过leftedge和rightedge告知发送端已经收到了哪些区间的数据报 。 因此 , 即使第5个包丢包了 , 当收到第6、7个包之后 , 接收端依然会告诉发送端 , 这两个包到了 。 剩下第5个包没到 , 就重传这个包 。 这个过程也叫做选择性重传(SACK , SelectiveAcknowledgment) , 它解决的是如何重传的问题 。
快速恢复
当然 , 发送端收到三次重复ACK之后 , 发现丢包 , 觉得现在的网络已经有些拥塞了 , 自己会进入快速恢复阶段 。
在这个阶段 , 发送端如下改变:
拥塞阈值降低为cwnd的一半cwnd的大小变为拥塞阈值cwnd线性增加以上就是TCP拥塞控制的经典算法:慢启动、拥塞避免、快速重传和快速恢复 。
011:能不能说说Nagle算法和延迟确认?
Nagle算法
试想一个场景 , 发送端不停地给接收端发很小的包 , 一次只发1个字节 , 那么发1千个字节需要发1000次 。 这种频繁的发送是存在问题的 , 不光是传输的时延消耗 , 发送和确认本身也是需要耗时的 , 频繁的发送接收带来了巨大的时延 。
而避免小包的频繁发送 , 这就是Nagle算法要做的事情 。
具体来说 , Nagle算法的规则如下:
当第一次发送数据时不用等待 , 就算是1byte的小包也立即发送后面发送满足下面条件之一就可以发了:数据包大小达到最大段大小(MaxSegmentSize,即MSS)之前所有包的ACK都已接收到延迟确认
试想这样一个场景 , 当我收到了发送端的一个包 , 然后在极短的时间内又接收到了第二个包 , 那我是一个个地回复 , 还是稍微等一下 , 把两个包的ACK合并后一起回复呢?
延迟确认(delayedack)所做的事情 , 就是后者 , 稍稍延迟 , 然后合并ACK , 最后才回复给发送端 。 TCP要求这个延迟的时延必须小于500ms , 一般操作系统实现都不会超过200ms 。
不过需要主要的是 , 有一些场景是不能延迟确认的 , 收到了就要马上回复:
接收到了大于一个frame的报文 , 且需要调整窗口大小TCP处于quickack模式(通过tcp_in_quickack_mode设置)发现了乱序包两者一起使用会怎样?
前者意味着延迟发 , 后者意味着延迟接收 , 会造成更大的延迟 , 产生性能问题 。
012.如何理解TCP的keep-alive?
大家都听说过http的keep-alive,不过TCP层面也是有keep-alive机制 , 而且跟应用层不太一样 。
试想一个场景 , 当有一方因为网络故障或者宕机导致连接失效 , 由于TCP并不是一个轮询的协议 , 在下一个数据包到达之前 , 对端对连接失效的情况是一无所知的 。 关注公众号:网络技术平台 , 回复“资料”获取网络资料 。
这个时候就出现了keep-alive,它的作用就是探测对端的连接有没有失效 。
在Linux下 , 可以这样查看相关的配置:
sudosysctl-a|grepkeepalive
//每隔7200s检测一次
net.ipv4.tcp_keepalive_time=7200
//一次最多重传9个包
net.ipv4.tcp_keepalive_probes=9
//每个包的间隔重传间隔75s
net.ipv4.tcp_keepalive_intvl=75
不过 , 现状是大部分的应用并没有默认开启TCP的keep-alive选项 , 为什么?
站在应用的角度:
7200s也就是两个小时检测一次 , 时间太长时间再短一些 , 也难以体现其设计的初衷,即检测长时间的死连接因此是一个比较尴尬的设计 。