1、介绍 TCP 连接的三次握手?追问:为什么 TCP 握手需要三次?
三次握手
(第1次握手)对于客户端,也是首先创建 传输控制块TCB,然后向服务器端发出 TCP 连接请求报文段 SYN 包,(其中,SYN 包 的初始序列号 seq=x,SYN 包中的同步位 SYN=1),发送完该数据包之后,客户端会进入 SYN_SENT 状态(同步已发送状态)。
这里要知道,TCP 协议规定,SYN 包(即同步位 SYN=1的报文段)不能够携带数据,但是要消耗掉一个序列号。
(第2次握手)服务器端接收到客户端发送过来的连接请求报文段SYN 包 过之后,则会同意建立TCP 连接,所以会反馈一个针对这个SYN 包的 确认应答信号ACK包(确认序列号是 ack =x+1);与此同时,服务器自己也会发送一个SYN 包 用来同步连接用(SYN 包的初始序列号 seq=y)。
因此,在第二阶段,服务器端会把 刚才的 ACK包和SYN包(确认位ACK=1,同步位SYN=1)统一起来形成一个包ACK+SYN包,作为一个包发送给客户端。发送完该数据包之后,服务器端进 SYN_RCVD状态(同步消息收到状态)。
(第3次握手)客户端接收到服务器端反馈回来的 ACK+SYN包 过之后,最后还要向服务器端给出确认,确认应答信号是 ACK包(确认序列号是ack=y+1,确认位ACK=1,而自己的数据包序列号seq=x+1)。 当这个包发送完毕后,客户端和服务器端就连接成功了,共同进入 ESTABLELISHED 状态(TCP 连接成功),完成了三次握手。之后的话,就是客户端与服务器端进行数据的交互了。
我们再来说一下为什么需要三次握手,而不是两次或者四次?
3次握手完成两个重要的功能,既要双方做好发送数据的准备工作(双方都知道彼此已准备好),也要允许双方就初始序列号进行协商,这个序列号在握手过程中被发送和确认。
如果为两次握手,如图所示。
对于客户端来讲,自己向服务器发送了SYN包,服务器会回应了一个ACK来应答客户端的请求并且服务器会发送一个自己的SYN包,来同步这个连接。客户端这里是收到了服务器的SYN包(序列号),并且也了解到服务器收到了自己的SYN包(序列号)。对于客户端而言,已经完成了要求。
但是对于服务端来讲,自己收到了客户端的SYN包(序列号),但是不确定客户端是否收到了自己ACK和SYN包(序列号)。如果是两次握手,这里就无法保证。对于服务端显然就不能满足要求。如果是三次握手,就解决了这个问题。
既然三次握手已经解决了问题,那么四次握手显然有点多余了。
2、介绍 TCP 断开的四次挥手,追问:为什么 TCP 的挥手需要四次?
当客户端和服务器端的数据交互完成过之后,通信双方就可以释放 TCP连接了。在释放 TCP连接之前,两者都是处于ESTABLISHED 状态的。
四次挥手:
(第1次挥手)首先,作为主动关闭TCP 连接的一方,客户端会先向服务器端(被动关闭的一方)发送一个FIN 包(序列号seq=u,TCP包首部结束位 FIN位=1) 作为己方准备与对端断开连接的数据包,它说明客户端已经没有数据再需要发送给服务器端了。之后,客户端会进入 FIN_WAIT_1 状态(终止等待1)。
这里要知道,TCP 协议规定,FIN 包即使不携带数据,也要消耗掉一个序列号。
(第2次挥手)服务器端收到客户端发送过来的 FIN包 过之后,会反馈一个对应的确认应答信号 ACK包(序列号seq=v,确认号ack=u+1,ACK位=1),然后,服务器端就进入 CLOSE_WAIT 状态 了(关闭等待状态)。
此时,从客户端到服务器端的输出流就关闭了,此时的 TCP 连接 是处于半关闭状态的,即客户端已经没有数据要发送给服务器端了,但是,若服务器端仍有数据需要发送给客户端,客户端仍然需要接收这个数据,也就是说从服务器端到客户端的这个方向上的连接并未关闭,而且这个状态可能会持续一段时间。
(第3次挥手)当客户端接收到服务器端反馈回来的确认应答信号 ACK包 过之后,就会进入 FIN_WAIT_2 状态 了(终止等待状态2),这时,客户端正在等待着服务器端的 FIN 包。与此同时,如果服务器端没有要向客户端发送的数据了,服务器端就可以向对端发送FIN包了(数据包的结束位 FIN位=1)。
现在假定这个 FIN包 的序列号是 seq=w(在半关闭状态时服务器端可能又发送了一些数据),同时数据包的确认号仍然是第二次握手中的 ack=u+1。当服务器端发送完这个 FIN包 过之后,服务器端就进入了LAST_ACK状态了(最后确认状态),等待着客户端发送过来最后的确认应答信号。
(第4次挥手)在客户端接收到服务器端的 FIN 包 过之后,必须要对这个 FIN 包 进行确认,因此,它会发送给服务器端一个对应的ACK 包(确认号 ack=w+1,序列号 seq=u+1),然后就会进入 TIME_WAIT 状态 了(时间等待状态),而不是直接进入 CLOSED 状态!
请注意,此时,TCP 连接还没有被释放掉,必须 经过 时间等待计时器(TIME_WAIT timer)设置的时间2MSL(Maximum Segment Lifetime)之后,客户端才能够进入 CLOSED 状态 。与此同时,一旦服务器端顺利接收到这个ACK 包 过之后,服务器端就进入CLOSED 状态了。
按道理来讲四次断开消息发送完成后,既可以进入到CLOSED状态,但是网络中情况复杂,有可能最后一个ACK丢失,服务器端会重复发送FIN包。所以要等待2MSL后才进入到CLOSED状态。如果直到2MSL,客户端都没有再次收到FIN,那么客户端推断ACK已经被服务端成功接收,则结束TCP连接。
另外,MSL指的是 最长分节生命长度,一般情况下被设置为2分钟。因此,从客户端进入到TIME_WAIT 状态 后,需要经过 2MSL (约等于4分钟)的时间才能进入到 CLOSED 状态,才能开始建立下一个新的TCP 连接。当客户端销毁掉自己的传输控制块TCB过之后,就彻底结束了这次的TCP连接。
有了三次握手的基础,我们对四次挥手就不难理解,这里客户和服务器分别向对方发送了一个FIN包用来断链,但是为了告知对方本端顺利的收到了消息,会分别对这个FIN报文进行ACK的确认,这就需要四次断开来实现,少一次都不行。
3、为什么连接的时候是三次握手,关闭的时候却是四次挥手?
因为当服务端收到客户端的SYN连接请求报文后,可以直接发送SYN+ACK报文。其中ACK报文是用来应答的,SYN报文是用来同步的。但是关闭连接时,当服务端收到FIN报文时,很可能并不会立即关闭SOCKET,所以只能先回复一个ACK报文,告诉客户端,"你发的FIN报文我收到了"。只有等到我服务端所有的报文都发送完了,我才能发送FIN报文,并不能一起发送。故需要四步断开。