挥手请求可以是Client端,也可以是Server端发起的,我们假设是Client端发起:
- 第一次挥手:Client端发起挥手请求,向Server端发送标志位是FIN报文段,设置序列号seq,此时,Client端进入FIN_WAIT_1状态,这表示Client端没有数据要发送给Server端了。
- 第二次挥手:Server端收到了Client端发送的FIN报文段,向Client端返回一个标志位是ACK的报文段,ack设为seq加1,Client端进入FIN_WAIT_2状态,Server端告诉Client端,我确认并同意你的关闭请求。
- 第三次挥手:Server端向Client端发送标志位是FIN的报文段,请求关闭连接,同时Client端进入LAST_ACK状态。
- 第四次挥手 :Client端收到Server端发送的FIN报文段,向Server端发送标志位是ACK的报文段,然后Client端进入TIME_WAIT状态,Server端收到Client端的ACK报文段以后,就关闭连接,此时,Client端等待2MSL的时间后依然没有收到回复,则证明Server端已正常关闭,那好,Client端也可以关闭连接了。
「为什么连接的时候是三次握手,关闭的时候却是四次握手?」
建立连接时因为当Server端收到Client端的SYN连接请求报文后,可以直接发送SYN ACK报文。其中ACK报文是用来应答的,SYN报文是用来同步的。所以建立连接只需要三次握手。
由于TCP协议是一种面向连接的、可靠的、基于字节流的运输层通信协议,TCP是全双工模式。
这就意味着,关闭连接时,当Client端发出FIN报文段时,只是表示Client端告诉Server端数据已经发送完毕了。当Server端收到FIN报文并返回ACK报文段,表示它已经知道Client端没有数据发送了,但是Server端还是可以发送数据到Client端的,所以Server很可能并不会立即关闭SOCKET,直到Server端把数据也发送完毕。
当Server端也发送了FIN报文段时,这个时候就表示Server端也没有数据要发送了,就会告诉Client端,我也没有数据要发送了,之后彼此就会愉快的中断这次TCP连接。
「为什么TIME_WAIT要等待2MSL?」
MSL:报文段最大生存时间,它是任何报文段被丢弃前在网络内的最长时间。
有以下两个原因:
- 第一点:保证TCP协议的全双工连接能够可靠关闭:由于IP协议的不可靠性或者是其它网络原因,导致了Server端没有收到Client端的ACK报文,那么Server端就会在超时之后重新发送FIN,如果此时Client端的连接已经关闭处于CLOESD状态,那么重发的FIN就找不到对应的连接了,从而导致连接错乱,所以,Client端发送完最后的ACK不能直接进入CLOSED状态,而要保持TIME_WAIT,当再次收到FIN的时候,能够保证对方收到ACK,最后正确关闭连接。
- 第二点:保证这次连接的重复数据段从网络中消失 如果Client端发送最后的ACK直接进入CLOSED状态,然后又再向Server端发起一个新连接,这时不能保证新连接的与刚关闭的连接的端口号是不同的,也就是新连接和老连接的端口号可能一样了,那么就可能出现问题:如果前一次的连接某些数据滞留在网络中,这些延迟数据在建立新连接后到达Client端,由于新老连接的端口号和IP都一样,TCP协议就认为延迟数据是属于新连接的,新连接就会接收到脏数据,这样就会导致数据包混乱,所以TCP连接需要在TIME_WAIT状态等待2倍MSL,才能保证本次连接的所有数据在网络中消失。
「RTT和RTO」
RTT:发送一个数据包到收到对应的ACK,所花费的时间
RTO:重传时间间隔(TCP在发送一个数据包后会启动一个重传定时器,RTO即定时器的重传时间)
开始预先算一个定时器时间,如果回复ACK,重传定时器就自动失效,即不需要重传;如果没有回复ACK,RTO定时器时间就到了,重传。
RTO是本次发送当前数据包所预估的超时时间,RTO不是固定写死的配置,是经过RTT计算出来的。
「滑动窗口」
TCP的滑动窗口主要有两个作用:
- 保证TCP的可靠性
- 保证TCP的流控特性
TCP报文头有个字段叫Window,用于接收方通知发送方自己还有多少缓存区可以接收数据,发送方根据接收方的处理能力来发送数据,不会导致接收方处理不过来,这便是流量控制。
发送方都维持了一个连续的允许发送的帧的序号,称为发送窗口;同时,接收方也维持了一个连续的允许接收的帧的序号,称为接收窗口。
发送窗口和接收窗口的序号的上下界不一定要一样,甚至大小也可以不同。
不同的滑动窗口协议窗口大小一般不同。
发送方窗口内的序列号代表了那些已经被发送,但是还没有被确认的帧,或者是那些可以被发送的帧
滑动窗口由四部分组成每个字节的数据都有唯一顺序的编码,随着时间发展,未确认部分与可以发送数据包编码部分向右移动,形式滑动窗口
- 绿色:发送成功并已经ACK确认的数据
- 黄色:发送成功等待ACK确认的数据(占用滑动窗口大小)
- 紫色:滑动窗口剩余大小可以发送的字节数量(滑动窗口可用大小)
- 灰色:后续数据编码
接收窗口的大小就是滑动窗口的最大值,数据传输过程中滑动窗口的可用大小是动态变化的。
但是还有这么一点,滑动窗口的设计仅仅是考虑到了处理方的处理能力,但是没有考虑到道路的通畅问题
就好像服务端可以处理100M数据,但是传输的数据99M都堵在路上了,这不就是导致道路阻塞了么?这就需要另外一个设计「拥塞避免」
「流量控制的目的」
如果发送者发送数据过快,接收者来不及接收,那么就会有分组丢失。
为了避免分组丢失,控制发送者的发送速度,使得接收者来得及接收,这就是流量控制。
流量控制根本目的是防止分组丢失,它是构成TCP可靠性的一方面。
「如何实现流量控制」
由滑动窗口协议(连续ARQ协议)实现。滑动窗口协议既保证了分组无差错、有序接收,也实现了流量控制。
主要的方式就是接收方返回的 ACK 中会包含自己的接收窗口的大小,并且利用大小来控制发送方的数据发送。
「流量控制引发的死锁」
当发送者收到了一个窗口为0的应答,发送者便停止发送,等待接收者的下一个应答。
但是如果这个窗口不为0的应答在传输过程丢失,发送者一直等待下去,而接收者以为发送者已经收到该应答,等待接收新数据,这样双方就相互等待,从而产生死锁。
为了避免流量控制引发的死锁,TCP使用了「持续计时器」。每当发送者收到一个零窗口的应答后就启动该计时器。时间一到便主动发送报文询问接收者的窗口大小。若接收者仍然返回零窗口,则重置该计时器继续等待;若窗口不为0,则表示应答报文丢失了,此时重置发送窗口后开始发送,这样就避免了死锁的产生。
拥塞控制「为什么要进行拥塞控制」
假设网络已经出现拥塞,如果不处理拥塞,那么延时增加,出现更多丢包,触发发送方重传数据,加剧拥塞情况,继续恶性循环直至网络瘫痪。
拥塞控制与流量控制的适应场景和目的均不同。
拥塞发生前,可避免流量过快增长拖垮网络;拥塞发生时,唯一的选择就是降低流量。
主要使用4种算法完成拥塞控制:
- 慢启动
- 拥塞避免
- 快重传算法
- 快速恢复算法
算法1、2适用于拥塞发生前,算法3适用于拥塞发生时,算法4适用于拥塞解决后(相当于拥塞发生前)。
「rwnd与cwnd」
rwnd(Receiver Window,接收者窗口)与cwnd(Congestion Window,拥塞窗口):
- rwnd是用于流量控制的窗口大小,主要取决于接收方的处理速度,由接收方通知发送方被动调整。
- cwnd是用于拥塞处理的窗口大小,取决于网络状况,由发送方探查网络主动调整。
同时考虑流量控制与拥塞处理,则发送方窗口的大小不超过min{rwnd, cwnd}。
「慢启动算法」
慢开始算法的思路就是,不要一开始就发送大量的数据,先探测一下网络的拥塞程度,也就是说由小到大逐渐增加拥塞窗口的大小。
这里用报文段的个数作为拥塞窗口的大小举例说明慢开始算法,实际的拥塞窗口大小是以字节为单位的。
一个传输轮次所经历的时间其实就是往返时间RTT,而且每经过一个传输轮次,拥塞窗口cwnd就加倍。
为了防止cwnd增长过大引起网络拥塞,还需设置一个慢开始门限ssthresh状态变量。
❝
ssthresh的用法如下:
❞
- cwnd<ssthresh时,使用慢开始算法。
- 当cwnd>ssthresh时,改用拥塞避免算法。
- 当cwnd=ssthresh时,慢开始与拥塞避免算法任意
注意,这里的慢并不是指cwnd的增长速率慢,而是指在TCP开始发送报文段时先设置cwnd=1,然后逐渐增大,这当然比按照大的cwnd一下子把许多报文段突然注入到网络中要慢得多。
「拥塞避免算法」
让拥塞窗口cwnd缓慢地增大,即每经过一个往返时间RTT就把发送方的拥塞窗口cwnd加1,而不是加倍。
这样拥塞窗口cwnd按线性规律缓慢增长,比慢开始算法的拥塞窗口增长速率缓慢得多
无论是在慢开始阶段还是在拥塞避免阶段,只要发送方判断网络出现拥塞(其根据就是没有按时收到确认,虽然没有收到确认可能是其他原因的分组丢失,但是因为无法判定,所以都当做拥塞来处理),就把慢开始门限ssthresh设置为出现拥塞时的发送窗口大小的一半(但不能小于2)。
然后把拥塞窗口cwnd重新设置为1,执行慢开始算法。
这样做的目的就是要迅速减少主机发送到网络中的分组数,使得发生拥塞的路由器有足够时间把队列中积压的分组处理完毕。
「整个拥塞控制的流程:」
假定cwnd=24时,网络出现超时(拥塞),则更新后的ssthresh=12,cwnd重新设置为1,并执行慢开始算法。
当cwnd=12=ssthresh时,改为执行拥塞避免算法
注意:拥塞避免并非完全能够避免了阻塞,而是使网络比较不容易出现拥塞。
「快重传算法」
快重传要求接收方在收到一个失序的报文段后就立即发出重复确认(为的是使发送方及早知道有报文段没有到达对方,可提高网络吞吐量约20%)而不要等到自己发送数据时捎带确认。
快重传算法规定,发送方只要一连收到三个重复确认就应当立即重传对方尚未收到的报文段,而不必继续等待设置的重传计时器时间到期
「快恢复算法」
快重传配合使用的还有快恢复算法,有以下两个要点:
- 当发送方连续收到三个重复确认时,就把ssthresh门限减半(为了预防网络发生拥塞)。
- 但是接下去并不执行慢开始算法
考虑到如果网络出现拥塞的话就不会收到好几个重复的确认,所以发送方现在认为网络可能没有出现拥塞。
所以此时不执行慢开始算法,而是将cwnd设置为ssthresh减半后的值,然后执行拥塞避免算法,使cwnd缓慢增大。