当前位置:   article > 正文

【计算机网络面试高频】-TCP协议常见问题详解,TCP数据报组成部分详解_fin ack

fin ack

3.TCP协议详解有关问题

首先,需要知道,我们程序的数据首先会打到TCP的Segment中,然后TCP的Segment会打到IP的Packet中,然后再打到以太网Ethernet的Frame中,传到对端后,各个层解析自己的协议,然后把数据交给更高层的协议处理。

3.1 TCP的报文头

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-8D6UucI0-1620830108364)(imgs\38.png)]

  • TCP的包是没有IP地址的,那是IP层上的事情,但是有源端口和目的端口;(一个TCP连接需要四个元组来表示是同一个连接(src_ip,src_port,dst_ip,dst_port))准确说是五元组,还有一个是协议。
  • 此外TCP报文头最重要的四个东西
    • seq(sequence number)是包的序号,用来解决网络包乱序的问题;
    • ack(acknowledgement number),用于确认收到,用来解决不丢包的问题;
    • window,也就是著名的滑动窗口(sliding window),用来解决流量控制;
    • tcp flag,也就是包的类型,主要是用来操控TCP的状态机的;
3.2 TCP的状态机

其实,网络上的传输是没有连接的,包含TCP也是一样的。而TCP所谓的“连接”,其实只不过是在通讯的双方维护一个连接状态,让它看上去好像有连接。所以,TCP的状态变换是非常重要的。

下图是 “TCP协议的状态机”“TCP建链接”“TCP断链接”“传数据” 的对照图。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-4WIi853y-1620830108376)(imgs\39.png)]

3.3 TCP的三次握手和四次挥手

3.3.1 TCP三次握手的过程是怎么样的?

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-zp8edwBO-1620830108381)(imgs\40.png)]

第一次握手:SYN=1 seq=x;

第二次握手:SYN=1 seq=y ACK=1 ack=x+1

第三次握手:seq=x+1 ACK=1 ack=y+1

三次的话都有seq来标识窗口的序号,也即SYN SYN-ACK ACK

为什么建立链接需要三次握手?

三次握手的目的是双方确认自己和对方的发送与接收都是正常的,以及最主要的是初始化Sequence Number,即seq。

初始化seq的初始值,通信的双方要互相通知对方自己的初始化syn,也就是x和y。这个号要作为自己的数据通信的序号,以保证应用层接收到的数据不会因为网络传输的问题而乱序。(TCP会用这个序号来拼接数据)。

之后进行数据的传输。

3.3.2 TCP四次挥手的过程是怎么样的?

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-iIgyU4iw-1620830108386)(imgs\41.png)]

第一次挥手:FIN=1 seq=x

第二次挥手:ACK=1 seq=y ack=x+1

第三次挥手:FIN=1 seq=z

第四次挥手:ACK=1 seq=x+1 ack=z+1

都有seq来标识窗口号,防止乱序。四次就是FIN ACK FIN ACK。

那为什么是四次挥手呢?

其实仔细观察四次挥手的话,就是2次。因为TCP是全双工的,关闭连接的话需要确保发送方和接收方都没有数据要发送了,即 发送方和接收方都需要Fin和Ack。只不过由有一方是被动的,所以看上去就成了所谓的4次挥手。

那为什么要等待TIME_WAIT呢即2MSL?

四次挥手发送完,客户端进入TIME_WAITING的状态,服务端收到ACK就CLOSED。而客户端需要在进入TIME_WAITING状态后等待2MSL才能关闭。

确保最后一个确认报文能够达到。 如果B没有收到A发送来的确认报文,那么就会重新发送连接释放请求报文,A等待这段时间就是为了处理这种情况;

3.3.3 关于TCP三次握手和四次挥手需要注意的几件事

  • 关于建连接时SYN超时: 如果server接收到了client发的SYN后,发回SYN-ACK后client掉线了,server端没有收到client回来的ACK,那么,这个连接处于一个中间状态,即每成功,也没失败。于是,server端如果在一定时间内没有收到的TCP会重发SYN-ACK。在Linux下,默认重试次数为5次,重试的间隔时间从1s开始都翻倍,5次的重试时间间隔为1s,2s,4s,8s,16s,总共31s,第5次发出后还要等32s才知道第5次也超时了,所以总共需要1+2+4+8+16+32=63s,TCP才会断开这个连接。

  • 关于SYN Flood攻击: 一些恶意的人就针对SYN 超时制造 SYN Flood攻击-给服务器发送一个SYN包后就下线,于是服务器需要默认等待63s才会断开连接,这样攻击者就可以把服务器的SYN连接的队列耗尽,让正常的连接请求不能处理。 于是Linux给其一个tcp_syncookies的参数来应对这个事-即当SYN队列满了之后,TCP会通过源端口、目的端口和时间戳 打造出一个特别的sequence number即seq发回去,如果是攻击者则不会有响应,如果是正常连接,则会把这个SYN cookie发回来,然后服务端可以通过cookie来建立连接(即使不在SYN队列中)。 但是这种应对措施只是适用于SYN Flood攻击的情况。**千万不要用tcp_syncookie来处理正常的大负载的连接的情况。**因为syncookies是妥协版本的TCP协议,并不严谨。对于正常的请求,我们可以调整TCP的参数,第一个:tcp_synack_retries可以用来减少重试的次数;第二个是:tcp_max_syn_backlog:可以增大SYN连接数;第三个是:tcp_abort_on_overflow处理不过来就干脆直接拒绝连接;

  • TIME_WAIT: 在TCP的状态图中,从TIME_WAIT状态到CLOSED状态,有一个超时设置,这个超时设置是2*MSL。(linux中设置成了30s)。为什么要有TIME-WAIT?为什么不直接转成CLOSED状态呢?主要有两个原因:1)TIME_WAIT确保了有足够的时间让对端收到了ACK,如果被动关闭的那放没有收到ACK,就会重发FIN,一来一去刚好2个MSL。2)有足够的时间让这个连接不会跟后面的连接混在一起(你要知道,有些自作主张的路由器会缓存IP数据报,如果连接被重用了,那么这些延迟收到的包就可能会跟新连接混在一起了。)

  • 关于TIME_WAIT数量太多: 如果在大并发的短链接下,TIME_WAIT就会太多,这回消耗很多资源。只要搜一下,你就会发现,十有八九的处理方式都是教你设置两个参数,一个叫tcp_tw_reuse,另一个叫tcp_tw_recycle的参数,这两个参数默认值都是被关闭的,后者recyle比前者resue更为激进,resue要温柔一些。另外,如果使用tcp_tw_reuse,必需设置tcp_timestamps=1,否则无效。这里,你一定要注意,打开这两个参数会有比较大的坑——可能会让TCP连接出一些诡异的问题(因为如上述一样,如果不等待超时重用连接的话,新的连接可能会建不上。正如官方文档上说的一样“It should not be changed without advice/request of technical experts”)。

    • 关于tcp_tw_reuse。官方文档上说tcp_tw_reuse 加上tcp_timestamps(又叫PAWS, for Protection Against Wrapped Sequence Numbers)可以保证协议的角度上的安全,但是你需要tcp_timestamps在两边都被打开(你可以读一下tcp_twsk_unique的源码 )。我个人估计还是有一些场景会有问题。
    • 关于tcp_tw_recycle。如果是tcp_tw_recycle被打开了话,会假设对端开启了tcp_timestamps,然后会去比较时间戳,如果时间戳变大了,就可以重用。但是,如果对端是一个NAT网络的话(如:一个公司只用一个IP出公网)或是对端的IP被另一台重用了,这个事就复杂了。建链接的SYN可能就被直接丢掉了(你可能会看到connection time out的错误)(如果你想观摩一下Linux的内核代码,请参看源码 tcp_timewait_state_process)。
    • 关于tcp_max_tw_buckets。这个是控制并发的TIME_WAIT的数量,默认值是180000,如果超限,那么,系统会把多的给destory掉,然后在日志里打一个警告(如:time wait bucket table overflow),官网文档说这个参数是用来对抗DDoS攻击的。也说的默认值180000并不小。这个还是需要根据实际情况考虑。

其实,如果服务器是HTTP服务器,那么设置一个HTTP的KeepAlive有多重要。(游览器会重用一个TCP连接来处理多个HTTP请求。)因为关闭连接是客户端发起的。它们之间的连接并不会主动关闭,后续的读写操作也会继续使用这个连接。而保持长连接,就需要用到TCP的保活功能。 TCP保活功能:主要为服务器应用提供,服务器应用希望知道客户主机是否崩溃,从而代表客户是否使用资源。

3.4 数据传输过程的SEQ(sequence number)

在TCP的报文段中,有一个序列号的来给每个发送的包编号,以此来保证了TCP发送的包不乱序。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-mS8r1Vua-1620830108389)(imgs/42.png)]

3.5 TCP重传机制

TCP要保证所有包都可以到达,所以必需要重传机制。

举例:接收端给发送端的ack确认智慧确认最后一个连续的包,比如发送端发了1,2,3,4,5一共5份数据,接收端收到了1,2,于是回ack 3,然后收到了4(注意此时3没收到),此时的TCP会怎么办呢?SeqNum和Ack是以字节数为单位,所以ack的时候,不能跳着确认,只能确认最大的连续收到的包,不然,发送端就以为之前的都收到了。

(1)重传算法-超时重传

一种是不回ack,死等3,当发送方发现收不到3的ack超时后,会重传3。一旦接收方收到3后,会ack 回 4——意味着3和4都收到了。

但是,这种方式会有比较严重的问题,那就是因为要死等3,所以会导致4和5即便已经收到了,而发送方也完全不知道发生了什么事,因为没有收到Ack,所以,发送方可能会悲观地认为也丢了,所以有可能也会导致4和5的重传。

对此有两种选择:

  • 一种是仅重传timeout的包。也就是第3份数据。
  • 另一种是重传timeout后所有的数据,也就是第3,4,5这三份数据。

这两种方式有好也有不好。第一种会节省带宽,但是慢,第二种会快一点,但是会浪费带宽,也可能会有无用功。但总体来说都不好。因为都在等timeout,timeout可能会很长(在下篇会说TCP是怎么动态地计算出timeout的)

(2)重传算法-快速重传机制

TCP引入了一种叫Fast Retransmit的算法,它不以时间为驱动,而是以数据驱动重传。

如果,包没有连续到达,就ack最后那个可能被丢了的包,如果发送方连续收到3次相同的ack,就重传。Fast Retransmit的好处是不用等timeout了再重传。

比如:如果发送方发出了1,2,3,4,5份数据,第一份先到送了,于是就ack回2,结果2因为某些原因没收到,3到达了,于是还是ack回2,后面的4和5都到了,但是还是ack回2,因为2还是没有收到,于是发送端收到了三个ack=2的确认,知道了2还没有到,于是就马上重转2。然后,接收端收到了2,此时因为3,4,5都收到了,于是ack回6。示意图如下:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-jtN7s6Os-1620830108391)(imgs\43.png)]

Fast Retransmit只解决了一个问题,就是timeout的问题,它依然面临一个艰难的选择,就是,是重传之前的一个还是重传所有的问题。对于上面的示例来说,是重传#2呢还是重传#2,#3,#4,#5呢?因为发送端并不清楚这连续的3个ack(2)是谁传回来的?也许发送端发了20份数据,是#6,#10,#20传来的呢。这样,发送端很有可能要重传从2到20的这堆数据。

(3)重传算法-SACK方法

Selective Acknowledgment (SACK)(参看RFC 2018),这种方式需要在TCP头里加一个SACK的东西,ACK还是Fast Retransmit的ACK,SACK则是汇报收到的数据碎版。参看下图:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-HzsDSnZU-1620830108392)(imgs\44.png)]

在发送端就可以根据回传的SACK来知道哪些数据到了,哪些没有到。于是就优化了Fast Retransmit的算法。当然,这个协议需要两边都支持。在 Linux下,可以通过tcp_sack参数打开这个功能(Linux 2.4后默认打开)。

这里还需要注意一个问题——接收方Reneging,所谓Reneging的意思就是接收方有权把已经报给发送端SACK里的数据给丢了。这样干是不被鼓励的,因为这个事会把问题复杂化了,但是,接收方这么做可能会有些极端情况,比如要把内存给别的更重要的东西。所以,发送方也不能完全依赖SACK,还是要依赖ACK,并维护Time-Out,如果后续的ACK没有增长,那么还是要把SACK的东西重传,另外,接收端这边永远不能把SACK的包标记为Ack。

注意: SACK会消费发送方的资源,试想,如果一个攻击者给数据发送方发一堆SACK的选项,这会导致发送方开始要重传甚至遍历已经发出的数据,这会消耗很多发送端的资源。

(4)重传算法-Duplicate SACK(DSACK) 重复收到数据的问题

Duplicate SACK又称D-SACK,其主要使用了SACK来告诉发送方有哪些数据被重复接收了

D-SACK使用了SACK的第一个段来做标志,

  • 如果SACK的第一个字段的范围不能被第二个字段覆盖,那么就是SACK 解读的话就是ACK之前的数据收到了,之后根据第一个字段和第二个字段的大小 来判断重复收到还是收到的情况。

  • 如果SACK的第一个段的范围被SACK的第二个段覆盖,那么就是D-SACK 解读的话就是ack之前的数据都收到了,后面那个重复收到了,缺失的部分丢失掉了,以及收到了。

例子1来理解ACK丢包的情况:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Q6ONsa9u-1620830108393)(imgs\45.png)]

由于ACK 4000大于[3000,3500],因此[4000, SACK=3000-3500]是D-SACK。发送端首先向接收端发送了3000-3499,3500-3999报文,接收端都收到了,但是接收端返回的ACK 3500及4000都丢失,导致发送端重传了3000-3499报文。接收端收到发送端重新发送的3000-3499报文,通过**[4000,SACK=3000-3500]**告知发送端,发送端就知道第一次的3000-3499报文接收端是收到了,由于当前ACK到了4000,那么4000之前的数据也都收到了。

例子2情况:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-At7LeW6C-1620830108394)(imgs\46.png)]

[4000, SACK=4500-5000]不满足D-SACK的条件,其是普通的SACK。而[4000, SACK=3000-3500, 4500-5000]是D-SACK,含义是:4000前的数据已收到,3000-3500的数据重复收到,4000-4499的包丢失,4500-5000的包收到。

例子3情况:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-YHBhlmFF-1620830108395)(imgs\47.png)]

[4000, SACK=4500-5000]及[4000, SACK=4500-5500]都是普通的SACK,[4000, SACK=5000-5500, 4500-5500]是D-SACK(第二判断方法),含义是:4000前的包收到,5000-5499包重复收到,4500-5500的包都收到,4000-4499的包丢失。

例子4情况:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-vnqt4szd-1620830108396)(imgs\48.png)]

接收端通过[1000, SACK=2000-2500]告知发送端1000前的报文及2000-2499报文已经收到,1000-1999的报文没有收到

于是接收端通过[1500, SACK=2000-2500]先告知发送端1500前的报文及2000-2499的报文都收到

D-SACK[2500, SACK=1000-1500]告知发送端2500前的报文全部收到,1000-1500的报文重复收到。

总之,引入了D-SACK,有这么几个好处:

  • 可以让发送方知道,是发出去的包丢了,还是回来的ACK包丢掉了;
  • 是不是自己的timeout太小了,导致重传。
  • 网络出现了先发的包后到的情况
  • 网络上是不是把我的数据包给复制了;

3.6 TCP的RTT时间

从前面的TCP重传机制知道TimeOut的设置对于重传非常重要。

  • 设长了,重发就慢,如果丢包老半天才重发,没有效率,性能差;
  • 设短了,会导致 可能并没有丢就重发。于是重发就越快,会增加网络拥塞,导致更多的超时,更多的超时导致更多的重发。

而且,这个超时时间在不同网络的情况下,根本没有办法设置一个固定的值。

为了动态地设置,TCP引入了RTT——Round Trip Time,也就是一个数据包从发出去到回来的时间。这样发送端就大约知道需要多少的时间,从而可以方便地设置Timeout——RTO(Retransmission TimeOut),以让我们的重传机制更高效。

听起来似乎很简单,好像就是在发送端发包时记下t0,然后接收端再把这个ack回来时再记一个t1,于是RTT = t1 – t0。没那么简单,这只是一个采样,不能代表普遍情况。

(1)经典算法

RFC793 中定义的经典算法是这样的:

  • 首先,先采样RTT,记下最近好几次的RTT值。

  • 然后做平滑计算SRTT( Smoothed RTT)。公式为:(其中的 α 取值在0.8 到 0.9之间,这个算法英文叫Exponential weighted moving average,中文叫:加权移动平均)

    SRTT = ( α * SRTT ) + ((1- α) * RTT)

  • 开始计算RTO。公式如下:

    RTO = min [ UBOUND, max [ LBOUND, (β * SRTT) ] ]

(2)Karn / Partridge 算法

但是上面的这个算法在重传的时候会出有一个终极问题——你是用第一次发数据的时间和ack回来的时间做RTT样本值,还是用重传的时间和ACK回来的时间做RTT样本值?

这个问题无论你选那头都是按下葫芦起了瓢。 如下图所示:

  • 情况(a)是ack没回来,所以重传。如果你计算第一次发送和ACK的时间,那么,明显算大了。
  • 情况(b)是ack回来慢了,但是导致了重传,但刚重传不一会儿,之前ACK就回来了。如果你是算重传的时间和ACK回来的时间的差,就会算短了。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-8VVCpqEn-1620830108397)(imgs\49.png)]

这个算法的最大特点是——忽略重传,不把重传的RTT做采样(你看,你不需要去解决不存在的问题)。

但是,这样一来,又会引发一个大BUG——如果在某一时间,网络闪动,突然变慢了,产生了比较大的延时,这个延时导致要重转所有的包(因为之前的RTO很小),于是,因为重转的不算,所以,RTO就不会被更新,这是一个灾难。 于是Karn算法用了一个取巧的方式——只要一发生重传,就对现有的RTO值翻倍(这就是所谓的 Exponential backoff),很明显,这种死规矩对于一个需要估计比较准确的RTT也不靠谱。

(3)Jacobson / Karels 算法

前面两种算法用的都是“加权移动平均”,这种方法最大的毛病就是如果RTT有一个大的波动的话,很难被发现,因为被平滑掉了。所以,1988年,又有人推出来了一个新的算法,这个算法叫Jacobson / Karels Algorithm(参看RFC6289)。这个算法引入了最新的RTT的采样和平滑过的SRTT的差距做因子来计算。 公式如下:(其中的DevRTT是Deviation RTT的意思)

RTT = S****RTT + α (RTT **– SRTT)** —— 计算平滑RTT

DevRTT = (1-β**)*DevRTT + β*(|RTT-SRTT|)** ——计算平滑RTT和真实的差距(加权移动平均)

RTO= µ * SRTT + ∂ *DevRTT —— 神一样的公式

(其中:在Linux下,α = 0.125,β = 0.25, μ = 1,∂ = 4 ——这就是算法中的“调得一手好参数”,nobody knows why, it just works…) 最后的这个算法在被用在今天的TCP协议中(Linux的源代码在:tcp_rtt_estimator

3.7 TCP流量控制-滑动窗口

TCP必需要解决的可靠传输以及包乱序(reordering)的问题,所以,TCP必需要知道网络实际的数据处理带宽或是数据处理速度,这样才不会引起网络拥塞,导致丢包。

TCP引入了一些技术和设计来做网络流控,Sliding Window是其中一个技术。 前面我们说过,TCP头里有一个字段叫Window,又叫Advertised-Window,这个字段是接收端告诉发送端自己还有多少缓冲区可以接收数据于是发送端就可以根据这个接收端的处理能力来发送数据,而不会导致接收端处理不过来。 为了说明滑动窗口,我们需要先看一下TCP缓冲区的一些数据结构:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-zpkolWup-1620830108398)(imgs\50.png)]

图中,我们可以看到:

  • 接收端LastByteRead指向了TCP缓冲区中读到的位置,NextByteExpected指向的地方是收到的连续包的最后一个位置,LastByteRcved指向的是收到的包的最后一个位置,我们可以看到中间有些数据还没有到达,所以有数据空白区。

  • 发送端的LastByteAcked指向了被接收端Ack过的位置(表示成功发送确认),LastByteSent表示发出去了,但还没有收到成功确认的Ack,LastByteWritten指向的是上层应用正在写的地方。

3.8 TCP拥塞控制

TCP通过滑动窗口来做流量控制,但是TCP觉得还不够,因为滑动窗口需要依赖与连接的发送端和接收端,其实并不知道网络中间发生了什么。

TCP觉得网络还应该知道整个网络上的事情。

具体举例场景:我们知道TCP通过一个timer采样了RTT并计算RTO,但是如果网络上的延时突然增加,那么TCP对这件事做出的应对就只有重传数据,但是重传会导致网络的负担更重,于是会导致更大的延迟以及更多的丢包,于是这个情况就会进入恶性循环被不断地放大。试想一下,如果一个网络内有成千上万的TCP连接都这么形事,那么会形成“网络风暴”,TCP这个协议就会拖垮整个网络。

所以,TCP不能无脑的一个劲儿重发数据,对网络造成更大的伤害。对此TCP的设计理念是:当拥塞发生的时候,要做自我牺牲。就像交通阻塞一样,每个车都应该把路让出来,而不要去抢路了。

拥塞控制的四个主要算法:(1)慢启动;(2)拥塞避免(3)拥塞发生;(4)快速恢复

(1)慢启动算法

TCP慢启动算法的意思是,刚刚加入网络的连接,一点一点的提速,不要一上来就告诉。

慢启动算法如下:

  • 连接建好的开始先初始化cwnd = 1,表明可以传一个MSS大小的数据。
  • 每当收到一个ACK,cwnd++; 呈线性上升
  • 每当过了一个RTT,cwnd = cwnd*2; 呈指数让升
  • 还有一个ssthresh(slow start threshold),是一个上限,当cwnd >= ssthresh时,就会进入“拥塞避免算法”(后面会说这个算法)

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-c06EVTm1-1620830108399)(imgs\51.png)]

(2)拥塞避免算法

前面说过,还有一个ssthresh(slow start threshold),是一个上限,当cwnd >= ssthresh时,就会进入“拥塞避免算法”。一般来说ssthresh的值是65535,单位是字节,当cwnd达到这个值时后,算法如下:

  • 收到一个ACK时,cwnd = cwnd + 1/cwnd
  • 当每过一个RTT时,cwnd = cwnd + 1

这样就可以避免增长过快导致网络拥塞,慢慢的增加调整到网络的最佳值。很明显,是一个线性上升的算法。

(3)拥塞状态时的算法

前面说过,丢包的时候,会有两种情况:

  • 等到RTO超时,重传数据包。TCP会认为这种情况太糟糕,反映也很强烈。
    • sshthresh=cwnd/2;
    • cwnd重置为1;
    • 进入慢启动过程;
  • 快重传,也就是在收到3个duplicate ACK时就开启重传,而不用等到RTO超时。
    • cwnd=cwnd/2
    • sshthread=cwnd
    • 进入快速恢复算法

(4)快速恢复算法-Fast Reconvery

快速重传和快速恢复算法一般同时使用。快速恢复算法是认为,你还有3个Duplicated Acks说明网络也不那么糟糕,所以没有必要像RTO超时那么强烈。 注意,正如前面所说,进入Fast Recovery之前,cwnd 和 sshthresh已被更新:

  • cwnd = cwnd /2
  • sshthresh = cwnd

然后,真正的Fast Recovery算法如下:

  • cwnd = sshthresh + 3 * MSS (3的意思是确认有3个数据包被收到了)
  • 重传Duplicated ACKs指定的数据包
  • 如果再收到 duplicated Acks,那么cwnd = cwnd +1
  • 如果收到了新的Ack,那么,cwnd = sshthresh ,然后就进入了拥塞避免的算法了。
声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/一键难忘520/article/detail/988078
推荐阅读
相关标签
  

闽ICP备14008679号