三次握手流程
1 | 客户端->服务端: SYNC=1,seq=x |
第一次握手:建立连接时,客户端发送sync包(syn=x)到服务器,并进入SYN_SENT状态,等待服务器确认;
第二次握手:服务器收到syn包,发送确认号(ack=x+1),同时自己也发送一个syn包(syn=y),即ACK+SYN包;服务器进入SYN_RECV状态。
第三次握手:客户端收到服务器的SYN+ACK包,向服务器发送确认包ACK(ack=y+1);发送完毕后,客户端和服务端都进入ESTABLISHED状态完成三次握手。
四次挥手流程
1 | 客户端->服务端: FIN=1,seq=u |
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
状态。
常见面试题
为什么连接的时候是三次握手,关闭的时候却是四次挥手?
当服务端收到客户端的SYN连接请求报文后,可以直接发送SYN+ACK报文。其中ACK报文是用来应答的,SYN报文是用来同步的。在关闭连接的时候,服务端接收到FIN报文后,很可能不会立即关闭SOCKET,所以先回复一个ACK报文,告诉客户端“你的FIN报文我收到了”。只有当服务端的报文都发送完,才发送FIN报文给客户端。
为什么TIME_WAIT状态需要经过2MSL才进入CLOSED状态?
虽然按道理,四个报文都发送完毕后,就可以直接进入CLOSED状态了,但是我们必须假设网络是不可靠的,有可能最后一个ACK丢失。由于网络原因,导致服务端没有收到客户端的ACK报文,不断的发送FIN给客户端。所以需要留下一段时间来处理这种情况,发送ACK报文给服务端。如果知道2MSL,客户端都没有再次收到FIN,那么客户端推断ACK已经被服务端成功接收,结束TCP连接。
为什么不能只用两次握手进行连接?
三次握手主要是完成两个重要功能:- 双方都已经做好了发送数据的准备工作
协商算法初始序列号,这个序列号在握手过程中发送和确认。
我们假设只进行两次握手;客户端发送连接请求给客户端,客户端收到后返回确认给客户端。
按照两次握手的约定,这个时候服务端已经认为成功的建立了连接,一直处于等待客户端发送消息的状态。
而客户端再丢失了服务端发送过来的确认报文,不知道是否建立成功,以及不知道服务端的序号状态。在这种情况下,客户端将认为连接没有建立成功,忽略客户端发送过来的任何数据报文,只等待应答报文。而服务端在发出数据报文超时后,不断的重复发送同样的数据。
如果已经建立连接后,客户端发生了故障怎么办?
TCP设有一个计时器,服务器每收到一个客户端请求后都会重新复位这个计时器,如果在规定时间内还没有收到客户端的任何数据,服务器就会发送一个探测报文,每隔75s发送一次。如果连续10次都没有反应,服务端就认为客户端发生了故障,关闭连接。