三次握手
在建立连接前,客户端和服务端需要通过握手来确认对方:
- 客户端发送syn(同步序列编号)请求,进入syn_send状态,等待确认;
- 服务端收到并确认syn包后发送syn+ack包,进入syn_recv状态;
- 客户端接收到syn+ack包后,发送ack包,双方进入链接状态;
四次挥手
- 客户端 - - FIN - - > 服务端。
此时,客户端停止发送数据,并且进入FIN_WAIT状态; - 服务端 - - ACK - - > 客户端。
服务端进入CLOSE_WAIT状态,但是如果服务端若发送数据,客户端依然要接受。 - 服务端 - - ACK, FIN - - > 客户端。
服务端将最后的数据发送完毕,向客户端发送连接释放报文,此时,服务端进入LAST_ACK 状态,等待客户端的确认。 - 客户端 - - ACK - -> 服务端, CLOSED
客户端收到连接释放报文,发出确认,当经过2*MSL(最长报文寿命)的时间段后,客户端进入CLOSED的状态。服务端收到ACK立马进入CLOSED状态。
服务端结束TCP连接的时间要比客户端早一点。
常见问题
1、为什么连接的时候是三次握手,而关闭的时候确是4次挥手?
答:因为当Server端收到Client端的SYN连接请求报文后,可以直接发送SYN+ACK报文。其中ACK报文是用来应答的,SYN报文是用来同步的。但是关闭连接时,当Server端收到Client端的FIN报文时,很可能并不会立即关闭SOCKET,所以只能先回复一个ACK报文,告诉Client端收到FIN报文,但是只有当Server端的所有报文都发送完毕才能发送FIN报文,因此不能一起发送,所以需要四步握手。
2、为什么TIME_WAIT状态需要经过2MSL(最大报文段生存时间)才能返回到CLOSE状态?
答:虽然按道理说四个报文都发送完毕后可以直接进入CLOSE状态了,但是我们需要假想网络不可靠,最后一个ACK报文可能丢失,所以TIME_WAIT状态就是用来重发可能丢失的ACK报文。
在Client端发送出最后的ACK回复,但是该ACK可能会丢失,Server端如果没有收到这个ACK,将不断的重复发送FIN片段,所以Client端不能立即关闭,它必须确认Server端收到了该ACK报文。Client端在发送出ACK报文后,会进入到TIME_WAIT状态。Client端会设置一个计时器,等待2MSL的时间,如果在该时间内再次收到FIN报文,那么Client端重新发送ACK报文并再次等待2MSL。如果直到2MSL结束都没有再收到FIN,那么Client端推测ACK已经被成功接收,那么结束TCP连接。
3、为什么不能用两次握手进行连接?
答:3次握手完成两个重要的功能,既要双方做好发送数据的准备工作(需要双方都知道彼此已经准备好),也要允许双方就初始序列号进行协商,这个序列号在握手过程中被发送和确认。
如果改为两次握手,那么可能会发生死锁。
4、如果已经建立了连接,但是客户端突然出现故障怎么办?
答:TCP还有一个保活计时器,显然,如果客户端出现故障,服务器不能一直等下去,白白浪费资源,所以,服务器每收到一次客户端的请求后都会重新复位这个计时器,时间通常设置为2小时,若两小时后还没有收到客户端的任何数据,服务器就会发送一个探测报文段,以后每隔75分钟发送一次,若一连发送10个探测报文仍然没有回应,服务器就会认为客户端出了状况,接着就关闭连接。