TCP传输控制协议
TCP工作在传输层,提供一种面向连接的、可靠的字节流服务。面向连接表示在进行数据传输之前必须先建立一个TCP连接,这就有了之后要讲的TCP三次握手。
TCP传输的信息单元称之为段(segment)。TCP提供点对点的服务,不支持多播和组播(UDP支持广播和多播)。
TCP提供确认重传机制,当TCP发送一个段(segment)后会启动一个定时器,超时后没有等到确认段则超时重传,所以说TCP为可靠的。
TCP的可靠性还体现在数据校验上,TCP首部会有检验和
字段会对TCP首部和数据进行检验(IP首部同样有校验和
,但仅对IP首部进行校验不对其数据部分进行校验,数据的校验交给TCP完成)。
TCP会对应用数据进行分割或填充,总是以TCP认为最合适的数据块大小进行发送。此外TCP还提供流量控制。
TCP首部结构
TCP首部如下图所示:
TCP头部如果不包含选项
通常是20字节(IP头部不含选项也是20字节)
- 源端口(16位):发送端的端口号。源端口及目的端口主要用于寻找发送端和接收端的应用程序。端口占16位表明端口最大为
2^16
(端口最大为65535)。 - 目的端口(16位): 接收端的端口号。TCP通过
src_ip, src_port, dst_ip, dst_port
四个元组唯一标识一个TCP连接。 - 序号(32位) & 确认序号(32位):之所以会有两个序号是因为TCP是全双工的。
序号
用来表明发送方向上的字节流而确认序号
用来表明接收方向上的字节流。数据
中的一个字节占用一个序号,若TCP数据
有100个字节则序号
就+100
。当数据
为0
时(无数据要传输只有IP首部和TCP首部),只要SYN
或FIN
标志为1
则序号
就要+1
(ACK
位为1
序号是不需要+1的)。
序号
和确认序号
变化过程:- 假定发送端的开始
序号
为SN
- 当发送端发送100字节数据,则下次的发送
序号
变为SN+100+1
- 当接收端成功接收100字节数据后,回复的
确认序号
为SN+100+1
;(确认序号
可理解为接收端告诉发送端下一次想接收的开始序号) - 当发送端收到接收端发来的TCP段,查看
确认序号
得知之前发送的100字节数据已经成功传输。
- 假定发送端的开始
- 首部长度(4位):TCP的
首部长度
字节和IP的首部长度
字节完全一致,既TCP首部
最大能有60字节
(包含选项
),一般正常的TCP首部
和IP首部
一样为20字节 - 保留(6位):保留未使用
- FLAG(6位):有6个flag,若某位被置
1
则表示使用此flag位- URG:紧急指针
- ACK:确认序号有效(除三次握手的第一个SYN包ACK位没有置1外,其余的所有包都要ACK标志)
- PSH:接收端应尽快将段传给应用层(此位用处不大)
- RST:重建连接
- SYN:同步序号(用于三次握手)
- FIN:发送端完成发送任务(主动断开TCP连接)
- 窗口大小(16位):申明窗口大小,用于TCP流量控制(滑动窗口流量控制)。主要作用是让接收端对发送端发送的数据进行控制。因为
窗口大小
字段占16位
所以窗口大小最大为65535
,但对于高速网络窗口太小,故在TCP选项
中有窗口扩大因子
用于扩大窗口大小,需要利用到此值来计算实际的窗口大小(具体见选项中的窗口扩大因子)。 - 校验和(16位):TCP的校验和是包含
TCP首部
和TCP数据
的校验。 - 紧急指针(16位):当
URG
被置1
时,此字段才有效,用于表明哪一段数据为紧急数据。 - 选项:
选项
字段最大为40字节
。选项
一般的结构是——|kind(1byte)|length(1byte)|info(n byte)|
,不同的kind
表示不同的作用,详见TCP选项。选着性挑常见的说明:kind=2
——最大报文段长度 MSS(Maximum Segment Size):表明最长报文大小。告诉对方本端能接收的最大长度的段是多少。一般以太网的MTU
为1500
则TCP的MSS
为1460
(除去IP首部和TCP首部,1500-20-20)。通常都是在SYN
中顺带声明MSS
。若应用层交给TCP的数据超过MSS
,TCP则会数据进行分割(让数据小于MSS后再传输),故TCP很少会有IP分片而UDP则会较多IP分片(UDP不会对数据进行分割,应用层传大多数据给UDP,UDP直接加个UDP首部后直接传给IP)kind=3
——窗口扩大因子 Window Scale:用于扩大窗口大小。TCP首部中窗口大小
字段占16位
最大窗口大小为65535
,但在高速网络中此大小可能不够用,需要用到窗口扩大因子
选项来增大TCP的窗口大小以便更快的传输数据。
Window Scale中有声明一个Shift count
,假设Shift count=n
,那么:实际窗口大小 = 窗口大小(16位) × 2^nkind=4
——选择性确认 SACK(Selective Acknowledgment):用于优化TCP传输性能。当TCP在传输数据时,如传输1 2 3 4
,假若1
报文丢失而2 3 4
报文顺利到达,TCP会重传1 2 3 4
。那么2 3 4
就重复传输了从而导致性能降低,使用SACK能使得接收方告诉发送方2 3 4
已收到仅重传1
即可。
Linux可通过修改/proc/sys/net/ipv4/tcp_window_scaling
启用或关闭SACK。
- 数据:TCP的
数据
部分是可选的,既TCP段是可以没有数据的,如三次握手的TCP段就没有数据
。
TCP连接状态
TCP是面向连接的,所以在开始传输数据前需要建立连接。而这个建立连接并非是在发送端和接收端创建一个专属的链路,只是发送端和接受端双方来维护一个连接状态,从而使得网络存在一个连接一样。TCP连接状态十分重要,建立连接(三次握手)以及释放连接(四次挥手)的详细过程如下图所示(图片来源)
- 当上一次收到的包是
SYN
或FIN
时,此次发送ACK的确认序号
为上次收到SYN
或FIN
包的序号
+1。如图中第一次握手时,Client发送SYN seq=x
。当Server收到此SYN
包后需要回一个确认ACK包,Server判断由于上次收到的是一个SYN
包,所以回复ACK
包的确认序号
为上次SYN
包中的序号
+1既x+1
- 通常在第一、二次握手的
SYN
包中,TCP首部
的选项
都会带有MSS
选项,分别告诉对方自己的最长报文大小是多少,然后协商用MSS小的为准进行传输。 - TCP三次握手其中一个很重要的作用是确定双方的初始化
序号
,既图中的x
及y
。借助此序号
保证应用层接收数据时不会乱序问题。 - 四次挥手,其实是两次的
FIN+ACK
。因为TCP是全双工的,所以当要断开连接时,Client——>Server方向需要一个FIN
,Server——>Client方向也需要一个FIN
- 当Client和Server同时断开,既同时发送
FIN
给对方时,TCP状态则不同于上图,Client和Server的状态都是FIN_WAIT_1——>CLOSING(收到对方的Fin)——>TIME_WAIT
- 在建立连接时,若其中一方发出
SYN
后对方没有响应,则发送SYN
的一方会尝试超时重传。重传采用的是一种称之为指数退避的策略,既重传的时间间隔为2的N次幂2^N
。第一次重传时间间隔2^0=1
(1秒后没收到ACK回应则重传),第二次重传时间间隔2^1=2
(第一次重传后2秒没有收到ACK回应则重传),第三次重传时间间隔2^2=4
(第二次重传后4秒没有收到ACK回应则重传)。至于会重传多少次则根据每个设备不同而不同,Linux可通过/proc/sys/net/ipv4/tcp_syn_retries
调节。 - TIME_WAIT状态也被称之为2MSL等待状态(Maximum Segment Lifetime),既TIME_WAIT状态要持续2MSL秒才能到CLOSED状态。之所以要等待2MSL秒有两个主要原因:
- Client发送的最后一个
ACK
有可能丢失,需要给Server足够的时间重传FIN
,一个ACK
和一个FIN
正好是2个MSL - 确保有足够时间使得该连接(src_ip, src_port, dst_ip, dst_port)不能被再次使用,假若不等待2MSL再释放,该连接(src_ip, src_port, dst_ip, dst_port)立马被新的程序所使用,由于网络存在延时上一次连接的数据报在新连接建立后才到达目的端,那么上次连接的数据将会和新连接混淆。
- Client发送的最后一个
- Linux内核中有很多专门针对TCP状态进行调整优化的选项,大多在
/proc/sys/net/ipv4/
目录下,其中各选项参数涉及复杂改天专门写一篇博客记录。 RST
复位报文,在TCP首部FLAG字段中有一个RST
复位字段,当报文发往基准的连接出现错误时,TCP就会发送一个复位报文。复位报文可以用来释放一个TCP连接,这种称之为异常释放(正常的释放都通过发送FIN
释放TCP连接的),正常释放会待双方数据都发送完毕后关闭,而异常释放则会丢弃待发送的数据。
TCP流量控制
TCP需要提供可靠的数据传输服务,那么就必须根据网络的实际情况及接受方数据的处理能力进行发送数据的调整,通过流量控制调整发送速率避免引起网络拥塞导致丢包。TCP是通过滑动窗口进行流量控制。
滑动窗口
在TCP首部
有窗口大小
字段,该字段是告诉对方自己的缓冲区还能接受多少数据,发送方根据接收方窗口的大小调整发送速率,防止发送方发送数据过快过多导致接收方缓冲区被占满无法接受处理新的数据。一般我们称接收方缓冲区空间大小的窗口为通告窗口。下面通过一个实例来了解TCP是如何利用滑动窗口来实现流量控制的
如上图所示,图中大致可分为三部分,框框即为滑动窗口:
1-3
部分:发送并已收到ACK
确认。这部分为已经成功传输的数据4-9
部分:此部分为在滑动窗口
中的数据,如图所示目前窗口大小为6
。其中又可细分为两部分:4-6
:已发送,但未收到ACK
确认7-9
:未发送,但接受方缓存区仍有空间,发送方可尽快将此部分数据发给接收方
10-11
部分:暂时不能发送,由于接受方缓存区空间不足而需要等待滑动窗口
移动
下面来描述一下滑动窗口
变化的一些过程:
- 发送方发送完数据
1 2 3
后,接收方回复ACK=4 window=6
,于是有了上图所示的情况,发送方准备再次发送4
以后的数据。 - 发送方发送
4 5 6
数据(发送方并不一定要一次性发送所有窗口大小的数据),此时滑动窗口大小为6
,窗口中的数据为4-9
,4 5 6
数据已发送但未确认,7 8 9
可选择性尽快发送。 - 接收方接受到
4 5 6
数据,根据自身缓冲区大小确定通告窗口大小并回复ACK=7 window=X
- 发送方接受到
ACK=7 window=X
的确认包,确定4 5 6
已成功传输,滑动窗口左边缘
向右移动到达数据7
的位置,根据ACK=7 window=X
调整滑动窗口
的大小- 若
ACK=7 window=3
,表明接收方确实已成功收到4 5 6
数据但应用程序没有将数据从缓冲区读走,所以窗口大小为3
(窗口缩小)。当前滑动窗口
中的数据为7 8 9
- 若
ACK=7 window=6
,表明接收方成功收到4 5 6
数据并被应用程序从缓冲区读走,窗口大小保持不变为6
。滑动窗口右边缘
向右滑动3
到达数据12
的位置。当前滑动窗口
中的数据为7-12
- 若
- 假定发送方收到的是
ACK=7 window=3
的确认包,当前滑动窗口
的大小为3
其中的数据只有7 8 9
,此时发送方一次性将7 8 9
全部发出 - 接收方收到
7 8 9
数据,缓冲区被填满。于是接收方回复ACK=10 window=0
,告诉发送方自身的缓冲区已满不要再发送数据了 - 发送方收到
ACK=10 window=0
的确认包,确定7 8 9
数据已成功传输,滑动窗口左边缘
向右移动到达数据10
的位置,发现接收方的窗口大小为0
表示已不能再接受数据了,于是停止发送数据并等待。 - 如果接收方缓冲区中的数据被读走,缓冲区有剩余空间,那么接收方会再次发送一个
ACK
包通告自己的窗口大小window=X
。发送方收到此确认包后调整滑动窗口
大小再发送数据,如此重复。
零窗口 与 糊涂窗口
TCP使用滑动窗口
来进行流量控制需要注意零窗口与糊涂窗口:
- 零窗口:如上面所说,当发送方窗口大小为
0
且接收方回复的window=0
表明无法再接收数据时,发送方会停止发送数据并等待。当接收方缓存区有空间后,接收方会回复一个ACK
给发送方告诉发送方自己的窗口大小让发送方发送数据。但如果接收方发送的这个ACK
包丢失了,那么发送方就以为接收方窗口一直为0
就无止境的等待。(TCP不对ACK
包进行确认保证,故此ACK
包一旦丢失发送方则无法知道接收方有可用缓存空间)
为防止此情况出现,TCP使用Zero Window Probe技术。当发送方窗口大小为0
时,发送方会向接收方发送零窗口探测包,让接收方回复ACK
通告它的窗口。有些设计会规定若三次探测包后接收方窗口大小依旧通告为0
,则发送方会发一个RST
复位报文中断连接。 - 糊涂窗口综合症:如上图的例子,当接收方通告自身窗口
window=0
时,发送方就会停止发送数据并等待。假若过了一段时间,接收方缓冲区有空间但空间很小比如说只有1
字节的空间,此时接收方通告自身窗口window=1
而发送方为了传输这1字节
的数据需要耗费40字节
开销(IP首部20字节+TCP首部20字节),这种花费大开销传输少数据的情况称之为糊涂窗口综合症。
为了解决糊涂窗口综合症,特意针对是发送方引起的还是接收方引起的糊涂窗口分为两种情况的解决方法:- 发送方引起的糊涂窗口:Nagle算法。1)等到 Window Size>=MSS 或是 Data Size >=MSS;2)等待时间或是超时200ms。两个条件满足其中一个就发数据,否则一直等待攒数据到满足条件为止。
- 接收方引起的糊涂窗口:接收方引起的糊涂窗口又可分为两种解决办法
- Clark解决方案:只要有数据到达就回复
ACK=X window=0
,通告自身窗口为0
让发送方停止发送数据,直到接收方Window Size>=MSS或缓冲区有一半空间 - 延迟确认:当有数据到达时不立即回复
ACK
,等待缓冲区有足够空间为止,为防止发送方超时重传,延时不能超过500毫秒
- Clark解决方案:只要有数据到达就回复
TCP重传机制
TCP是一个可靠的传输协议,为了保证数据成功到达接收方就必须有数据丢失后的重传机制,因为1)数据可能在传输过程中丢失,2)即使接收方成功接受到数据,当回复ACK
时,这个ACK
数据包也可能会丢失。
TCP重传机制是通过在发送数据时设置定时器来解决丢包问题,当定时器
溢出时仍没有收到ACK
确认,TCP则认为该数据已经丢失便会重新传输该数据。TCP重传机制的重点在于如何确定超时时间和如何确定重传频率。
TCP重传机制的定时器可分为四种类型:
- 重传定时器
- 坚持定时器
- 保活定时器
- 2MSL定时器
重传定时器
为了确认数据是否丢失或被丢弃而设定,是对数据ACK
确认包等待的时间。当TCP发送数据时,就创建针对该数据的重传定时器。若定时器超时前收到ACK
确认包,则撤消重传定时器;若定时器超时后还没有收到ACK
确认包,则重传该数据,并将重传定时器复位重新计时。
如果重传失败,TCP会尝试多次重传,至于重传多少次后放弃则不同系统设计不同,Linux系统与/proc/sys/net/ipv4/tcp_retries1
(放弃回应一个TCP连接请求前﹐需要进行多少次重试) 、/proc/sys/net/ipv4/tcp_retries2
(在丢弃激活(已建立通讯状况)的TCP连接之前﹐需要进行多少次重试)有关,关于这两个选项参数更详细内容请点击这里
TCP多次重传的间隔并不都是一样的,TCP重传间隔采用指数退避(exponential backoff)策略,既两次重传时间间隔是一个倍乘关系(每次增加1倍)。如书中的例子重传时间间隔为1 3 6 12 24 48 64 64 64 64...
;Linux下可能是这样1 2 4 8 16 32 64 64*8
(tcp_retries2
默认为15
)。
坚持定时器
其实坚持定时器
在零窗口的时候就已经提到过。当接收方的窗口大小为0
时,会通告自身通告窗口
已为0
让发送方停止发送数据。等到接收方的缓冲区空闲通告窗口
恢复后,接收方会发送ACK
包通告自身窗口大小。因为TCP是不会对ACK
包进行确认的,既这个带通告窗口
大小的ACK
包可能会丢失。假若此ACK
包丢失,发送方认为接收方窗口仍为0
继续等待,接收方认为自己已经通知发送方自己窗口大小而等待发送方的数据,双方都陷入等待当中。为了避免这个ACK
包丢失而导致的双方等待,当接收方通告自身窗口大小为0
时,发送方会启用坚持定时器,当坚持定时器超时后,发送方会主动发送探测包
询问接收方的窗口大小。在计算坚持定时器时同样是使用指数退避,比如说首次坚持定时器超时时间为1.5秒
,则后续超时时间为3 6 12
。但不会一直增加下去坚持定时器总是在5~60
之间,达到60
后就一直以60
为超时时间。
保活定时器
其实TCP的保活定时器就是类似于HTTP的Keepalive功能,保活定时器是用于检测对方是否发送异常,如果有异常则及时关闭连接释放资源。当保活定时器超时后会检查连接是否空闲太久,若空闲时间超过设定时间则发送探测报文。通过对方是否响应、响应是否符合预期,来判断对方是否正常,若不正常则关闭连接释放资源。
在Linux系统中是由/proc/sys/net/ipv4/tcp_keepalive_time
、/proc/sys/net/ipv4/tcp_keepalive_intvl
、/proc/sys/net/ipv4/tcp_keepalive_probes
三个内核参数决定。
- /proc/sys/net/ipv4/tcp_keepalive_time
最后一次数据交换到TCP发送第一个保活探测报文的时间,即允许连接空闲的时间,默认为7200s - /proc/sys/net/ipv4/tcp_keepalive_intvl
保活探测报文的重传时间,默认为75s - /proc/sys/net/ipv4/tcp_keepalive_probes
保活探测报文的发送次数,默认为9次
默认情况下Linux的一次完整的保活探测时间,既Linux下TCP认为一个连接出现异常后关闭的时间:
tcp_keepalive_time + tcp_keepalive_intvl × tcp_keepalive_probes = 7200 + 75 × 9
= 7875
2MSL定时器
TCP的TIME_WAIT状态
也称之为2MSL等待状态
,之前的章节也已说过。TCP连接的一端发起主动关闭,在四次挥手主动关闭方发送完最后一个ACK
包后既进入TIME_WAIT
状态,必须等待2MSL
的时间后才能释放该TCP连接。之所以必须等待2MSL
时间主要有以下两个原因:
- 给被动关闭方重传
FIN
包的机会。主动关闭方发送的最后一个ACK
包有可能会丢失,假若该最后一个ACK
包丢失,被动关闭方的重传定时器
超时后仍没有收到该ACK
包就会重传之前的FIN
。假若主动关闭方在发送完最后一个ACK
后释放连接而此ACK
丢失,就会导致被动关闭方处在CLOSE_WAIT
状态,重传定时器
不断超时、FIN
指数退避不断重传,Linux系统中直到被动关闭方达到tcp_retries2
次数后,TCP认为此连接出现问题才会释放该连接。 - 防止已失效连接的旧数据包出现在新连接上。TCP是根据四元组(src_ip,src_port,dst_ip,dst_port)唯一标识一个连接,数据包可能因为网络等原因延时到达,假若主动关闭方在发送最后一个
ACK
后立刻释放连接使得原先的端口可以被使用,当应用程序使用原先被释放的端口新建了一个TCP连接而旧连接的数据延时到达,这样就会造成数据混淆。
超时重传 与 快速重传
介绍完四个定时器后,接下来看看TCP常见的两种重传机制——超时重传、快速重传
- 超时重传:超时重传机制利用到的就是
重传定时器
,当重传定时器
超时后仍没有收到ACK
确认则认为该数据包已经丢失,发送方会重传该数据包并将重传定时器
复位重新计算。至于重传定时器
的*超时时间则是由Jcobson/Karels 算法计算而得。 - 快速重传:由于
超时重传
是以时间作为驱动,需要等待timeout后才进行重传。为了更快的得知数据是否已经丢失,TCP还会使用快速重传机制。快速重传是以数据为驱动的,如果发送方连续收到3次相同的ACK,则认为数据包已经丢失需要重传。
比如说发送方发送1 2 3 4 5
数据,但2
丢失了3 4 5
成功到达,则3 4 5
到达时接收方回复的都是ACK=2
。发送方收到三个重复的ACK则知道2
已丢失需要重传。
所以只要是重传定时器
超时或收到重复ACK
,TCP则会认为数据丢失需要重传数据。
TCP拥塞控制
TCP流量控制主要是针对发送端和接收端进行,而TCP拥塞控制则是对其中的网络情况进行控制。试想一下,当网络负载严重导致网络拥塞,进而使得数据丢失或延时到达,假若TCP不加控制就超时重传数据,那便会加重网络负载致使恶性循环直至整个网络瘫痪。为了避免此情况出现,TCP拥塞控制的主要实现方法有以下四种:
- 慢启动
- 拥塞避免
- 快速重传
- 快速恢复
慢启动
慢启动,既当刚刚需要发送数据时,不是一下便发送大流量数据而是一点点的进行提速。
慢启动为TCP的发送方定义了一个窗口——拥塞窗口(Congestion Window),简称cwnd,拥塞窗口表明发送方一次可以发送多少报文段,一般cwnd是根据MSS而定,每收到一个ACK
则cwnd=cwnd+cwnd
。
慢启动理想状态的过程如下所示():
- TCP三次握手建立连接后,cwnd=1,表明可以发送一个MSS大小的数据。假定MSS=1024,cwnd=1则可发送
1024(1)
大小的数据 - 当收到
ACK
后,cwnd=1+1,表明可以发送两个MSS大小的数据。cwnd=2可发送2048(2)
大小的数据 - 当收到cwnd=2时发送数据的
ACK
后,cwnd=2+2,表明可以发送四个MSS大小的数据。cwnd=4可发送4096(4)
大小的数据 - 当收到cwnd=4时发送数据的
ACK
后,cwnd=4+4,表明可以发送八个MSS大小的数据。cwnd=8可发送8192(8)
大小的数据 - cwnd呈指数增长。但并不会无限增长下去,当拥塞窗口<通告窗口时,发送拥塞窗口大小数据;但当拥塞窗口>通告窗口时,只能发送通告窗口大小数据
拥塞避免 快速重传 快速恢复
TCP对于拥塞避免有一套复杂的拥塞避免算法,在此只能简要的描述TCP是如何实现拥塞避免的。
TCP的拥塞避免算法通常都是与慢启动一起作用,那么每个TCP连接就会维持两个变量——cwnd
(拥塞窗口Congestion Window)、ssthresh
(慢启动门限slow start threshold),TCP拥塞避免大致过程如下:
- 当TCP完成三次握手后,会给该连接设定两个变量
cwnd
和ssthresh。cwnd
初始化值为1个报文段
(该值通常为MSS-40[IP+TCP header]),ssthresh
初始化值一般为65535字节
- 首先TCP使用慢启动,
cwnd
指数增长,但cwnd
并不会无限制增长,当cwnd
>
接收方的通告窗口时以通告窗口为标准。拥塞窗口是发送方的流量控制、通告窗口是接收方的流量控制。当拥塞窗口<
通告窗口时,由拥塞窗口(发送方)控制发送速率(既为慢启动);当拥塞窗口>
通告窗口时,由通告窗口(接收方)控制发送速率。 - 假设通过窗口无限大,慢启动过程中
cwnd
一直呈指数增长,但一直指数增长下去终会达到网络最大负载而导致网络崩溃,为了防止此情况发生会设置一个慢启动门限——ssthresh
,当cwnd
到达ssthresh
既cwnd==ssthresh
时,进入拥塞避免算法(每收到一个ACK
不再像慢启动那样+1cwnd
而是+1/cwnd
,既cwnd = cwnd + 1/cwnd
),进入拥塞避免后呈线性增长。 - 上面说的都是在拥塞没有发送的情况下,但一旦拥塞出现TCP则会有响应的措施。TCP在两种情况下会认为数据包已经丢失(详看重传机制)——超时 或 收到3个重复ACK,此时TCP认为已进入拥塞状态。TCP对这两种情况处理又所有不同:
- 假若在慢启动过程中出现超时
- 出现超时后则需要重新设置ssthresh。数据包超时后慢启动门限ssthresh会被设置为当前拥塞窗口的一半,既
ssthresh=cwnd/2
cwnd
重新初始化,既cwnd
=1。重新开始慢启动过程- 重新慢启动后,
cwnd
指数增长。当cwnd
=
ssthresh
时,停止慢启动进入拥塞避免算法 - 进入拥塞避免算法后,每收到一个
ACK
不再像慢启动那样+1cwnd
而是+1/cwnd
,既cwnd = cwnd + 1/cwnd
- 出现超时后则需要重新设置ssthresh。数据包超时后慢启动门限ssthresh会被设置为当前拥塞窗口的一半,既
- 假若在慢启动过程中收到重复
ACK
- 收到
3
个重复ACK
后需要重新设置ssthresh,ssthresh=cwnd/2
。进入快速恢复算法 - 在快速恢复算法中,
cwnd
会被重新计算——cwnd = ssthresh(既上面的cwnd/2) + 3*MSS(3个ACK)
- 启用快速重传重新丢失的数据包
- 如果在收到重复
ACK
则cwnd=cwnd+1MSS
- 如果收到新
ACK
则cwnd=ssthresh
,然后进入拥塞避免算法(既每收到一个ACK
则cwnd=cwnd+1/cwnd
)
- 收到
- 假若在慢启动过程中出现超时
超时:慢启动
——>超时
——>ssthresh=cwnd/2 重新慢启动
——>cwnd=ssthresh
——>拥塞避免算法(cwnd=cwnd+1/cwnd)
重复ACK:慢启动
——>3重复ACK
——>快速恢复算法+快速重传 ssthresh=cwnd/2
——>cwnd=ssthresh+3MSS
——>拥塞避免算法(cwnd=cwnd+1/cwnd)
当cwnd
<=
ssthresh
时,慢启动(指数增长);当cwnd
>
ssthresh
,拥塞避免算法(线性增长)