三次握手和四次挥手机制(重点)

TCP是可靠的双向通道传输协议,所以需要三次握手建立连接和四次挥手关闭连接来保证信息传输的可靠性。

报文标志位

先来了解一下TCP在握手连接时候的报文用到的标志位。

  • SYN:建立连接消息的标志
  • ACK(Acknowledgment Number):应答消息的标志
  • FIN:连接关闭的标志
  • URG:紧急消息标志
  • RST:重置连接标志
  • PSH:缓冲区尚未填满标志

三次握手过程

tcp3.jpg

第一次握手:建立连接。客户端发送SYN连接请求报文段,将SYN位置为1,Sequence Number(seq)置为x;然后,客户端进入SYN_SEND状态,等待服务器的确认;

第二次握手: 服务器收到客户端的SYN报文段。服务器需要对这个SYN报文段进行确认,设置为ack=x+1(seq+1);同时,自己还要发送SYN+ACK报文段,来应答建立连接请求,将SYN+ACK位置为1,seq置为y;服务器端将上述所有信息放到一个报文段(即SYN+ACK报文段)中,一并发送给客户端,此时服务器进入SYN_RECV状态;

第三次握手: 客户端收到服务器的SYN+ACK报文段。然后设置ack=seq+1,向服务器发送ACK报文段,这个报文段发送完毕以后,客户端和服务器端都进入ESTABLISHED状态,完成TCP三次握手。

为什么要三次握手?

为了防止已失效的连接请求报文段突然又传送到了服务端,因而产生错误。如果只有两次握手,client发送连接请求后不再ACK服务端的SYN。此时client由于自身的原因判断建立连接失败,可能会重复建立TCP连接,而服务端却会认为那些被client丢弃的TCP连接依然有效,会浪费服务端资源。

具体例子: “已失效的连接请求报文段”的产生在这样一种情况下:client发出的第一个连接请求报文段并没有丢失,而是在某个网络结点长时间的滞留了,以致延误到连接释放以后的某个时间才到达server。本来这是一个早已失效的报文段。但server收到此失效的连接请求报文段后,就误认为是client再次发出的一个新的连接请求。于是就向client发出确认报文段,同意建立连接。假设不采用“三次握手”,而是“两次”,那么只要server发出确认,新的连接就建立了。由于现在client并没有发出确认建立连接的请求,因此不会理睬server的确认,也不会向server发送数据。但server端新的运输连接已经建立,并一直等待client发来数据。这样,server的很多资源就白白浪费掉了。采用“三次握手”的办法可以防止上述现象发生。例如刚才那种情况,client不会向server的确认发出确认。server由于收不到确认,就知道client并没有要求建立连接。

四次挥手过程

tcp4.jpg
第一次挥手: client发送关闭连接请求。向server发送一个FIN报文段,设置Sequence Number;此时,client进入FIN_WAIT_1状态;这表示client没有数据要发送给server了;

第二次挥手: server收到了client发送的FIN报文段,向client返回ACK报文段, 设置ack=seq+1,seq=v;client进入FIN_WAIT_2状态;server告诉client,我“同意”你的关闭请求,但是你要等一下,我还有东西要给你;

第三次挥手: server向client发送FIN+ACK报文段,设置ack=seq+1,seq=w;确认没有数据要传输了,可以进行关闭连接,同时server进入LAST_ACK状态,等待client确认收到最后的数据;

第四次挥手: client收到server发送的FIN报文段,向server发送ACK报文段,然后client进入TIME_WAIT状态;server收到主机1的ACK报文段以后,就关闭连接;此时,client等待2MSL后依然没有收到回复,则证明Server端已正常关闭,那好,client也可以关闭连接了。

为什么要四次挥手?

TCP协议是一种面向连接的、可靠的、基于字节流的运输层通信协议。TCP是全双工模式,这就意味着,当主机1发出FIN报文段时,只是表示主机1已经没有数据要发送了,主机1告诉主机2,它的数据已经全部发送完毕了;但是,这个时候主机1还是可以接受着来自主机2的数据;当主机2返回ACK报文段时,表示它表示已经知道主机1没有数据发送了,但主机2还是有数据要发送数据到主机1的;当主机2也发送了FIN报文段时,这个时候就表示主机2也没有数据要发送了,就会告诉主机1,我也没有数据要发送了,之后彼此就会愉快的中断这次TCP连接。

为什么要等待2MSL?

TIME_WAIT的作用

MSL(Maximum Segment Lifetime):报文段最大生存时间,它是任何报文段被丢弃前在网络内的最长时间。
原因有二:

  • 保证TCP协议的全双工连接能够可靠关闭
  • 保证这次连接的重复数据段从网络中消失

第一点:如果client直接CLOSED了,那么由于IP协议的不可靠性或者是其它网络原因,导致server没有收到client最后回复的ACK。那么server就会在超时之后继续发送FIN,此时由于client已经CLOSED了,就找不到与重发的FIN对应的连接。所以,client不能直接进入CLOSED,而是要保持TIME_WAIT,当再次收到FIN的时候,能够保证对方收到ACK,最后正确的关闭连接。
第二点:如果client直接CLOSED,然后又再向server发起一个新连接,我们不能保证这个新连接与刚关闭的连接的端口号是不同的。也就是说有可能新连接和老连接的端口号是相同的。一般来说不会发生什么问题,但是还是有特殊情况出现:假设新连接和已经关闭的老连接端口号是一样的,如果前一次连接的某些数据仍然滞留在网络中,这些延迟数据在建立新连接之后才到达server,由于新连接和老连接的端口号是一样的,TCP协议就认为那个延迟的数据是属于新连接的,这样就和真正的新连接的数据包发生混淆了。所以TCP连接还要在TIME_WAIT状态等待2倍MSL,这样可以保证本次连接的所有数据都从网络中消失。

TIME_WAIT和CLOSE_WAIT的区别在哪

  • CLOSE_WAIT是被动关闭形成的;当客户端close socket而发送FIN报文过来时,服务端回应ACK之后进入CLOSE_WAIT状态。随后检查是否存在未传输数据,如果没有则发起第三次挥手,发送FIN+ACK报文给对方,进入LAST_ACK状态并等待对方ACK报文到来。
  • TIME_WAIT是主动关闭连接方式形成的;处于FIN_WAIT_2状态时,收到对方FIN+ACK报文后进入TIME_WAIT状态;之后再等待两个MSL(Maximum Segment Lifetime:报文最大生存时间)。