目录
  1. 1. TCP传输控制协议
    1. 1.1. TCP首部结构
    2. 1.2. TCP连接状态
    3. 1.3. TCP流量控制
      1. 1.3.1. 滑动窗口
      2. 1.3.2. 零窗口 与 糊涂窗口
    4. 1.4. TCP重传机制
      1. 1.4.1. 重传定时器
      2. 1.4.2. 坚持定时器
      3. 1.4.3. 保活定时器
      4. 1.4.4. 2MSL定时器
      5. 1.4.5. 超时重传 与 快速重传
    5. 1.5. TCP拥塞控制
      1. 1.5.1. 慢启动
      2. 1.5.2. 拥塞避免 快速重传 快速恢复

TCP传输控制协议

 TCP工作在传输层,提供一种面向连接的、可靠的字节流服务。面向连接表示在进行数据传输之前必须先建立一个TCP连接,这就有了之后要讲的TCP三次握手
 TCP传输的信息单元称之为段(segment)。TCP提供点对点的服务,不支持多播和组播(UDP支持广播和多播)。
 TCP提供确认重传机制,当TCP发送一个段(segment)后会启动一个定时器,超时后没有等到确认段则超时重传,所以说TCP为可靠的。
 TCP的可靠性还体现在数据校验上,TCP首部会有检验和字段会对TCP首部和数据进行检验(IP首部同样有校验和,但仅对IP首部进行校验不对其数据部分进行校验,数据的校验交给TCP完成)。
 TCP会对应用数据进行分割或填充,总是以TCP认为最合适的数据块大小进行发送。此外TCP还提供流量控制。

TCP首部结构

 TCP首部如下图所示:
tcp-header
 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首部),只要SYNFIN标志为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):表明最长报文大小。告诉对方本端能接收的最大长度的段是多少。一般以太网的MTU1500则TCP的MSS1460(除去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^n
    • kind=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连接状态十分重要,建立连接(三次握手)以及释放连接(四次挥手)的详细过程如下图所示(图片来源)


tcp-open-close

  • 当上一次收到的包是SYNFIN时,此次发送ACK的确认序号为上次收到SYNFIN包的序号+1。如图中第一次握手时,Client发送SYN seq=x。当Server收到此SYN包后需要回一个确认ACK包,Server判断由于上次收到的是一个SYN包,所以回复ACK包的确认序号为上次SYN包中的序号+1x+1
  • 通常在第一、二次握手的SYN包中,TCP首部选项都会带有MSS选项,分别告诉对方自己的最长报文大小是多少,然后协商用MSS小的为准进行传输。
  • TCP三次握手其中一个很重要的作用是确定双方的初始化序号,既图中的xy。借助此序号保证应用层接收数据时不会乱序问题。
  • 四次挥手,其实是两次的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)立马被新的程序所使用,由于网络存在延时上一次连接的数据报在新连接建立后才到达目的端,那么上次连接的数据将会和新连接混淆。
  • Linux内核中有很多专门针对TCP状态进行调整优化的选项,大多在/proc/sys/net/ipv4/目录下,其中各选项参数涉及复杂改天专门写一篇博客记录。
  • RST复位报文,在TCP首部FLAG字段中有一个RST复位字段,当报文发往基准的连接出现错误时,TCP就会发送一个复位报文。复位报文可以用来释放一个TCP连接,这种称之为异常释放(正常的释放都通过发送FIN释放TCP连接的),正常释放会待双方数据都发送完毕后关闭,而异常释放则会丢弃待发送的数据。

TCP流量控制

 TCP需要提供可靠的数据传输服务,那么就必须根据网络的实际情况及接受方数据的处理能力进行发送数据的调整,通过流量控制调整发送速率避免引起网络拥塞导致丢包。TCP是通过滑动窗口进行流量控制。

滑动窗口

 在TCP首部窗口大小字段,该字段是告诉对方自己的缓冲区还能接受多少数据,发送方根据接收方窗口的大小调整发送速率,防止发送方发送数据过快过多导致接收方缓冲区被占满无法接受处理新的数据。一般我们称接收方缓冲区空间大小的窗口为通告窗口。下面通过一个实例来了解TCP是如何利用滑动窗口来实现流量控制的
tcp-slide-window
 如上图所示,图中大致可分为三部分,框框即为滑动窗口

  • 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-94 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。两个条件满足其中一个就发数据,否则一直等待攒数据到满足条件为止。
    • 接收方引起的糊涂窗口:接收方引起的糊涂窗口又可分为两种解决办法
      1. Clark解决方案:只要有数据到达就回复ACK=X window=0,通告自身窗口为0让发送方停止发送数据,直到接收方Window Size>=MSS缓冲区有一半空间
      2. 延迟确认:当有数据到达时不立即回复ACK,等待缓冲区有足够空间为止,为防止发送方超时重传,延时不能超过500毫秒

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时间主要有以下两个原因:

  1. 给被动关闭方重传FIN包的机会。主动关闭方发送的最后一个ACK包有可能会丢失,假若该最后一个ACK包丢失,被动关闭方的重传定时器超时后仍没有收到该ACK包就会重传之前的FIN。假若主动关闭方在发送完最后一个ACK后释放连接而此ACK丢失,就会导致被动关闭方处在CLOSE_WAIT状态,重传定时器不断超时、FIN指数退避不断重传,Linux系统中直到被动关闭方达到tcp_retries2次数后,TCP认为此连接出现问题才会释放该连接。
  2. 防止已失效连接的旧数据包出现在新连接上。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而定,每收到一个ACKcwnd=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完成三次握手后,会给该连接设定两个变量cwndssthreshcwnd初始化值为1个报文段(该值通常为MSS-40[IP+TCP header]),ssthresh初始化值一般为65535字节
  • 首先TCP使用慢启动cwnd指数增长,但cwnd并不会无限制增长,当cwnd>接收方的通告窗口时以通告窗口为标准。拥塞窗口是发送方的流量控制、通告窗口是接收方的流量控制。当拥塞窗口 < 通告窗口时,由拥塞窗口(发送方)控制发送速率(既为慢启动);当拥塞窗口 > 通告窗口时,由通告窗口(接收方)控制发送速率。
  • 假设通过窗口无限大,慢启动过程中cwnd一直呈指数增长,但一直指数增长下去终会达到网络最大负载而导致网络崩溃,为了防止此情况发生会设置一个慢启动门限——ssthresh,当cwnd到达ssthreshcwnd==ssthresh时,进入拥塞避免算法(每收到一个ACK不再像慢启动那样+1cwnd而是+1/cwnd,既cwnd = cwnd + 1/cwnd),进入拥塞避免后呈线性增长。
  • 上面说的都是在拥塞没有发送的情况下,但一旦拥塞出现TCP则会有响应的措施。TCP在两种情况下会认为数据包已经丢失(详看重传机制)——超时收到3个重复ACK,此时TCP认为已进入拥塞状态。TCP对这两种情况处理又所有不同:
    1. 假若在慢启动过程中出现超时
      • 出现超时后则需要重新设置ssthresh。数据包超时后慢启动门限ssthresh会被设置为当前拥塞窗口的一半,既ssthresh=cwnd/2
      • cwnd重新初始化,既cwnd=1。重新开始慢启动过程
      • 重新慢启动后,cwnd指数增长。当cwnd = ssthresh时,停止慢启动进入拥塞避免算法
      • 进入拥塞避免算法后,每收到一个ACK不再像慢启动那样+1cwnd而是+1/cwnd,既cwnd = cwnd + 1/cwnd
    2. 假若在慢启动过程中收到重复ACK
      • 收到3个重复ACK后需要重新设置ssthreshssthresh=cwnd/2。进入快速恢复算法
      • 快速恢复算法中,cwnd会被重新计算——cwnd = ssthresh(既上面的cwnd/2) + 3*MSS(3个ACK)
      • 启用快速重传重新丢失的数据包
      • 如果在收到重复ACKcwnd=cwnd+1MSS
      • 如果收到新ACKcwnd=ssthresh,然后进入拥塞避免算法(既每收到一个ACKcwnd=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,拥塞避免算法(线性增长)

Powered: Hexo, Theme: Nadya remastered from NadyMain