三次握手和四次挥手

三次握手流程

1
2
3
4
5
6
7
客户端->服务端: SYNC=1,seq=x
Note left of 客户端: SYCN_SEND
服务端-->客户端: SYNC=1,ACK=1,seq=y,ack=x+1
Note right of 服务端: SYNC_RECV
客户端->服务端: ACK=1,seq=x+1,ack=y+1
Note left of 客户端: ESTAB-LISHED
Note right of 服务端: ESTAB-LISHED

第一次握手:建立连接时,客户端发送sync包(syn=x)到服务器,并进入SYN_SENT状态,等待服务器确认;

第二次握手:服务器收到syn包,发送确认号(ack=x+1),同时自己也发送一个syn包(syn=y),即ACK+SYN包;服务器进入SYN_RECV状态。

第三次握手:客户端收到服务器的SYN+ACK包,向服务器发送确认包ACK(ack=y+1);发送完毕后,客户端和服务端都进入ESTABLISHED状态完成三次握手。

四次挥手流程

1
2
3
4
5
6
7
8
9
客户端->服务端: FIN=1,seq=u
Note left of 客户端: FIN_WAIT_1
服务端-->客户端: ACK=1,seq=v,ack=u+1
Note right of 服务端: CLOSE_WAIT
服务端-->客户端: FIN=1,ACK=1,seq=w,ack=u+1
Note right of 服务端: LAST_ACK
客户端->服务端: ACK=1,seq=u+1,ack=w+1
Note right of 服务端: CLOSED
Note left of 客户端: 等待2MSL后,进入CLOSED

1、 客户端进程发送连接释放报文,并且停止发送数据。报文中带FIN=1标记。并且其序列号seq=u(等于前面已经传送过来的数据的最后一个字节的序号+1)。客户端进入FIN-WAIT=1状态。

2、服务器接收到连接释放报文后,发送确认报文ACK=1,ack=u+1,并带上自己的序列号seq=v,此时,服务端进入CLOSE-WAIT状态。这个时候处于半关闭状态;表示客户端不再发送数据,服务端不再接收数据;服务端若要发送数据,客户端依然要接收。

3、 客户端手抖服务端的确认请求后,进入FIN-WAIT-2状态,等待服务器发送连接释放报文。

4、 服务器将数据发送完毕后,就向客户端发送连接释放报文,FIN=1,ack=u+1,seq=w。此时服务端进入LAST-ACK状态,等待客户端的确认。

5、 客户端收到服务端的连接释放报文后,必须发出确认。ACK=1,ack=w+1,seq=u+1,客户端进入TIME-WAIT状态。此时TCP连接还没有释放,必须经过2*MSL的时间后,才进入CLOSED状态。

6、 服务端接收到客户端发出的确认后,立即进入CLOSED状态。

常见面试题

  1. 为什么连接的时候是三次握手,关闭的时候却是四次挥手?
    当服务端收到客户端的SYN连接请求报文后,可以直接发送SYN+ACK报文。其中ACK报文是用来应答的,SYN报文是用来同步的。

    在关闭连接的时候,服务端接收到FIN报文后,很可能不会立即关闭SOCKET,所以先回复一个ACK报文,告诉客户端“你的FIN报文我收到了”。只有当服务端的报文都发送完,才发送FIN报文给客户端。

  2. 为什么TIME_WAIT状态需要经过2MSL才进入CLOSED状态?
    虽然按道理,四个报文都发送完毕后,就可以直接进入CLOSED状态了,但是我们必须假设网络是不可靠的,有可能最后一个ACK丢失。

    由于网络原因,导致服务端没有收到客户端的ACK报文,不断的发送FIN给客户端。所以需要留下一段时间来处理这种情况,发送ACK报文给服务端。如果知道2MSL,客户端都没有再次收到FIN,那么客户端推断ACK已经被服务端成功接收,结束TCP连接。

  3. 为什么不能只用两次握手进行连接?
    三次握手主要是完成两个重要功能:

    • 双方都已经做好了发送数据的准备工作
    • 协商算法初始序列号,这个序列号在握手过程中发送和确认。

      我们假设只进行两次握手;客户端发送连接请求给客户端,客户端收到后返回确认给客户端。
      按照两次握手的约定,这个时候服务端已经认为成功的建立了连接,一直处于等待客户端发送消息的状态。
      而客户端再丢失了服务端发送过来的确认报文,不知道是否建立成功,以及不知道服务端的序号状态。在这种情况下,客户端将认为连接没有建立成功,忽略客户端发送过来的任何数据报文,只等待应答报文。而服务端在发出数据报文超时后,不断的重复发送同样的数据。

  4. 如果已经建立连接后,客户端发生了故障怎么办?
    TCP设有一个计时器,服务器每收到一个客户端请求后都会重新复位这个计时器,如果在规定时间内还没有收到客户端的任何数据,服务器就会发送一个探测报文,每隔75s发送一次。如果连续10次都没有反应,服务端就认为客户端发生了故障,关闭连接。