慢启动阶段cwnd指数变化,曲线对应y=2的x次方(x代表发送方发送次数)
拥塞避免阶段cwnd线性变化,曲线对应y=2的ssthresh次方+x,(x代表拥塞阶段的发送次数)
慢启动阶段的超时未确认 ssthresh=cwnd/2,cwnd=1,进入慢启动,此时慢开始门限ssthresh的值降低为cwnd的一半,是一个小于8的值
拥塞避免阶段的超时未确认 ssthresh=cwnd/2,cwnd=1,进入慢启动,此时慢开始门限ssthresh的值降低为cwnd的一半,是一个大于8的值,猜测应该是小于16的值
慢启动阶段的重复确认3次及以上 ssthresh=cwnd/2,cwnd/=2,相当于直接从慢启动转向拥塞避免的曲线上,同时唤起快重传、快恢复、拥塞避免算法,此后执行拥塞避免算法,cwnd++
拥塞避免阶段的重复确认3次及以上 ssthresh=cwnd/2,cwnd/=2 ,相当于直接从慢启动转向拥塞避免的曲线上,同时唤起快重传、快恢复、拥塞避免算法,执行拥塞避免算法,cwnd++
超时算法:超时后,隔一定时间后进行重传(具体重传时间间隔待定)
快重传算法:丢包时唤起
快恢复算法:丢包时唤起,防止继续丢包,通过调整当前阶段变化为拥塞避免阶段和cwnd值减小一半来实现
以下来自CSDN的deoxyribonucleicacid:
原文链接:https://blog.csdn.net/qq_39937902/article/details/82495527
TCP选项:
MSS(maximum segment size)最大报文传输段(数据报不分片的最大值):32bit数据部分字段(不包括TCP头).
应用层往TCP层写数据时,若TCP发送缓冲区满,但还没write完,内核便将write系统调用挂起;TCP发送缓冲区满或网络空闲时,缓冲区数据便被通过ip层发送到链路层发送队列中;发送缓冲区数据成功发送(包括重传等情况)后,才唤醒write.而ip分片会导致TCP重传可能性增大,此时ip层再分片,将可能导致write一直挂起,性能下降.
SACK(selective acknowledgements)选择确认选项.34bytes
目的是为了不重传已经正确到达的数据.eg:A发送1,2,3,4,5数据,B接收到1,3,5,而2,4丢失.不启动SACK,A从2开始重传;启动SACK,A只重传2,4.
tcp首部options最多40bytes,而指明一个序列号边界需4bytes,一个报文需要2个边界:如2丢失,需要1,3序列号指明.故需要8bytes.因此最多指明4个丢失报文.(因为还需要2个功能字节,故40/8-1=4).这个选项大小是34bytes.
时间戳选项(timestamps).10bytes.
1,用来计算往返时间RTT.发送方在放松报文前放入当前时间,接收方只需在应答时复制该时间戳即可,以便发送方计算RTT.
2,PAWS防止回绕的序号:众所周知,32bit序列号在数据超过2^32便会重复.在高速网络(如1Gb/s),则几秒便会重复,从而可能带来传输的混乱.启用时间戳选项后,可以很容易的分辨出相同序列号的数据报先后顺序.
窗口扩大选项(window scaling):TCP最大窗口64Kb,对于时延和带宽都大的通信不能满足性能和高吞吐量,于是窗口扩大选项产生.
窗口扩大选项占3bytes,第一个byte表示移位值S.表示win左移S bit.在tcp握手时协商从而扩大,后续如果改为0,则回复win的16bit大小.S最大值是14,因此win最大是65535*2^14=1GB.
拥塞窗口cwnd(congestion window):发送方维持一个动态变化的拥塞窗口,大小取决于网络拥塞程度.
发送方的发送窗口<=拥塞窗口.一般两个窗口相等.
网络良好:从而拥塞窗口较大时,由于接收方接收能力有限,从而发送窗口达到一定值不再发生变化.
网络拥塞:发送方发送一些报文段,没有在一定时间内接收到确认报文,可认为网络拥塞.
慢启动算法:为了防止网络拥塞,cwnd=1,发送报文后收到确认报文,则cwnd*=2;
拥塞避免:慢启动算法还是增加过快,容易导致网络拥塞.因此加了个状态变量:慢开始门限ssthresh
当cwnd < ssthresh时,使用慢启动算法
当cwnd > ssthresh时,使用拥塞避免算法.
拥塞避免算法:每次往返 cwnd+1,而不再翻倍.cwnd此时增长比慢启动慢的多.
拥塞避免算法之AIMD算法:和式增加,积式减少.
积式减少:网络一旦出现超时:ssthresh=cwnd/2,cwnd=1,执行慢启动算法.(cwnd
快重传算法:
发送方发送1,2,3,4,5,6,7,8,9,10,…连续收到3个确认4后,不必再等待超时,直接从5开始重发.
5丢失…
接收方接收到1,2,3,4,6(确认4),7(确认4),8(确认4),9(确认4)…
快恢复算法:
发送方连续收到3个确认后,执行积式减少:ssthresh=cwnd/2,此时cwnd/=2,开始执行拥塞避免算法,拥塞窗口cwnd缓慢增大.
以下来自简书:
作者:LayneWang
链接:https://www.jianshu.com/p/3735bfe22a3c
Tcp的慢开始和拥塞避免
1、发送端主机在确认发送报文段的速率时,既要根据接收端的接收能力,又要从全局考虑不要使网络发生拥塞。
2、因此,每一个tcp连接需要有以下两个状态变量:a、接收端窗口。b、拥塞窗口。
3、接收端窗口:是接收端根据其目前的接收缓存大小所许诺的最新窗口值,是来自接收端的流量控制。接收端将此窗口值放在TCP报文的首部中的窗口字段,传给发送端。
4、拥塞窗口:是发送端根据自己估计的网络拥塞程度而设置的窗口值,是来自发送端的流量控制。
5、发送端的发送窗口的上限值取自接收端窗口和拥塞窗口两者中较小的一个。
慢开始算法原理
1、在主机刚刚开始发送报文段时,可先把拥塞窗口的值设置为一个最大报文段的MSS的数值。
2、在每收到一个对新的报文段的确认后,将拥塞窗口增加至多一个最大报文段MSS的数值。
3、用这种方法逐步增大发送端的拥塞窗口,这可以使分组注入到网络的速率更加合理。
慢开始阶段和拥塞避免阶段
1、当TCP连接进行初始化时,将拥塞窗口置为1。慢开始的门限值ssthresh初始值设置为16 。
2、发送端的发送窗口不能超过拥塞窗口和接收窗口中的最小值。假定接收端窗口值足够大,那么发送窗口的值就等于拥塞窗口的数值。
3、在执行慢开始算法时,拥塞窗口初始值为1 。 发送第一个报文段M0 。 在发送端收到ACK(接收端确认收到M0,期望收到M1)后,拥塞窗口增大到2,发送端连续发送M1和M2两个报文,这样拥塞窗口成指数增长,报文发送量同时增长。直至达到慢开始的门限值ssthresh。然后进入拥塞避免阶段。此时,改为拥塞窗口按线性规律增长。
4、当拥塞窗口增大到一定程度时,发生超时(拥塞)时,更新门限值ssthresh变为超时时的0.5 并执行慢开始算法。
拥塞避免算法
1、乘法减小
a、“乘法减小”指不论在慢开始阶段还是拥塞避免阶段,只要出现一次超时(即网络拥塞),就把慢开始门限值ssthresh设置为当前拥塞窗口值乘以0.5。
b、当网络频繁出现拥塞时,ssthresh值就下降的很快,以大大减少注入到网络中的分组数。
2、加法增大
“加法增大”是指执行拥塞避免算法后,当收到对所有报文段的确认就将拥塞窗口增加一个报文MSS大小,是拥塞窗口缓慢增大,以防止网络过早出现拥塞。
“拥塞避免”并非指能完全避免拥塞。只能控制拥塞窗口按线性增长规律,使网络比较不容易出现拥塞。
以下来自:
原文链接:https://blog.csdn.net/weixin_39687783/article/details/80725802
把书读薄(TCP/IP详解 卷一 第二十一章)
为什么需要重传?
TCP本身需要提供可靠的服务,方式之一就是确认接收方真的收到了数据,如果过了一段时间,即超时了,还没有收到确认的报文,认为报文可能被丢失,就重新传送报文,确保数据都能被收到
超时发生重传不一定重传同样的报文段,可以重新分组发送一个较大的报文段,只要它不超过接收方声明的MSS
为什么要动态的计算超时时间?
网络流量和路由器在包的传输过程中可能改变,因此RTT(Round Trip Time)也会变化,如果超时时间保持不变,假如RTT变的大了,可能出现ACK还在再发送的路上,却直接重发了包,造成不必要的浪费
如何动态计算超时重传时间?
TCP经典算法RTT是:R <- αR + (1-α)M,重传时间为 RTO=Rβ
其中M表示测量时间意思是发送一个某序列号的字节和接收到包含该序列号的确认之间的往返时间
α是一个推荐值为0.9的平滑因子: 对公式稍微做变形为 R <- (1-g)R + gM 并重新组合R<-R+g(M-R) ,假设R是对下一个测量结果的预估,那么 M-R 表示预估的结果和真实结果的偏差,整个公式所表明的就是预估的结果加上部分的预估偏差即为新的预估结果。偏差可能包括两部分,1是噪音,具有一定的随机性,用Er表示,2是预估的错误,选用的R初始值有问题,用Ee表示,则 R<-R+g*Er+g*Ee ,对于Ee来讲,它的目标是把预估的结果往正确的方向去靠拢,而Er由于存在一定的随机性,经过多个样本之后,可能最终影响的结果都互相抵消,那么对于预估来讲,希望Ee的因子要大些,Er的因子要小些,使得R的最终取值能够朝着正确的平均值去收敛,而对于Er来讲无论g取何值都会使得结果往好的方向走,因而取g为0.1-0.2能做一个好的值,也就是说α取值为0.9,0.8即可
R是估算的RTT的平均值
RTO表示重传超时时间(Retransmission Timeout)意思是如果超过这个时间还没有收到ack就重新发送
β 是RTT的变异系数,当传输时间可以忽略不计的时候,最大时延和平均时延的变化最大,可以看做所有的时延都是因为处理所造成的,这个时候最大值是平均值的两倍,推荐β取值为2。【假设往返时间最大值是R,如果传输时延忽略不计,那么这两次变化的平均传输时延就是0.5R,也就说最大值是平均值的两倍】
β取值,详见 https://tools.ietf.org/html/rfc813 第五章 和 jacobson算法 http://www.cs.binghamton.edu/~nael/cs428-528/deeper/jacobson-congestion.pdf
这种衡量方式没有考虑到,RTT变化范围很大的时候,经典的RTO的变化跟不上,从而引起不必要的重传,此时网络已经处于饱和状态,再重传更会增加网络负载
jacobson算法中提到 β取值为2,此时的负载最多为30%,远不能处理真实的情况
另一个没有没有解决的问题是,假定一个分组被发送,当超时发生时,分组以更长的RTO进行重传,然后收到一个确认,那么收到的这个ACK是针对第一个分组还是第二个分组呢?这种场景的解决方式是Karn算法,主要思想是超时和重传发生时,在重传数据的确认最后到达之前,不能更新RTT估算值
tcp协议当前实现估算超时时间的方法是什么?
使用jacobson算法,RTO依赖于被平滑的RTT和被平滑的均值偏差,而不是均值的常数倍
实现代码https://elixir.bootlin.com/linux/v2.6.32/ident/tcp_rtt_estimator
如何避免分组被丢弃?
使用拥塞避免算法,它假定分组丢失就是因为网络发生了拥塞。发送方使用两个变量来做拥塞控制,一个是拥塞窗口cwnd,一个是慢启动阈值ssthresh,当cwnd小于等于ssthresh时使用慢启动,否则使用拥塞避免算法 。原则如下:
1. 发送方发送的字节小于等于cwnd和接收方通告窗口大小的最小值
2. 发生超时,即在超时定时器溢出时还没有收到ACK,ssthresh被设置为当前窗口大小的一半,cwnd被设置为1个报文段
3. 接收到新ACK时,如果cwnd<=ssthresh,就执行慢启动,cwnd值加1,否则执行拥塞控制,cwnd增加1/cwnd
cwnd值加1会造成窗口按照指数方式增长,比如刚开始是1,那么当它收到ack之后,下次发送两个包,然后会收到两个ack,cwnd立马增长为4,依此类推
cwnd值增加1/cwnd是一种加性增长,每接收到cwnd个包才加1
收到重复ack之后怎么处理?
收到一个重复ack之后,其实无法确认是报文丢失还是报文段重新排序引起的,因此会等待少量重复ack到来,一般会等待3个或者以上。如果连续收到3个或以上的重复ack,则判定可能报文丢失了,选择立马重传,而不需要等待超时定时器溢出,这种方式称为快速重传算法。接下来执行快速恢复算法,两者合并整个过程如下:
1. 收到3个重复的ack之后,将ssthresh的值设置为当前拥塞窗口的一半。重传丢失的报文段,设置cwnd为ssthresh加上3倍的报文段大小【重发了3个,所以增加3个报文段】
2. 每收到另一个重复的ack,cwnd就增加1个报文段大小,如果新的cwnd允许发送,则发送1个分组
3. 确认一个新的ACK到达时,设置cwnd为第一步中设置的ssthresh大小。
这个新的ACK应该是确认第一步中丢失的报文那一刻起发送的报文到第一步中重发的报文期间所有报文,包括第一步中重发的报文。
收到重复ack之后不执行慢启动(即设置cwnd为1),是因为收到重复的ack仅仅表示网络中有数据丢失了。对于接收方而言,只有收到另一个报文段才会产生重复的ack,而该报文已经已经离开网络并进入接收方的缓存,说明,收发端之间数据仍然在流动,不需要执行慢启动来突然减少数据流。 详见 https://tools.ietf.org/html/rfc2001 第4章
整个过程图例如下
状态转移为:
如何对上述过程中涉及的指标进行初始化?
在较新的TCP实现中,有一个路由表来维持指标,包括:被平滑的RTT、被平滑的均值偏差以及慢启动门限。一个TCP连接关闭时,如果已经发送了16个窗口的数据(这就足够多了),且目的节点的路由表不是默认的表向,就会存储起来。建立连接是(部分主动还是被动),只要路由表中有对应的值,就用它初始化
TCP是如何处理给定连接返回的ICMP差错的?
TCP常见的ICMP差错包括源站抑制、主机不可达和网络不可达
1. 主机不可达和网络不可达实际上都被忽略,因为它们被认为是一种短暂现象(可能场景包括路由器替换需要花费几分钟才恢复)。此时的TCP连接没有关闭,反而会发送引起差错的数据
2. 源站抑制引起cwnd被设置为1个报文段大小,从而发起慢启动,但是慢启动的ssthresh不会变化