tcp握手挥手的状态图如下:
close()
发送 FIN 请求主动关闭连接,之后进入 FIN_WAIT1
状态,等待远程TCP连接中断请求的确认。
如果服务器出现shutdown再重启,使用netstat -nat
查看,就会看到很多FIN-WAIT-1的状态。就是因为服务器当前有很多客户端连接,直接关闭服务器后,无法接收到客户端的ACKCLOSE_WAIT
.接下来呢,会需要检查自己是否还有数据要发送给对方,如果没有的话,那就可以close()
这个SOCKET并发送FIN
报文给对方,即关闭自己到对方这个方向的连接。有数据的话则看程序的策略,继续发送或丢弃。简单地说,当处于CLOSE_WAIT
状态下,需要完成的事情是等待你去关闭连接,并发送 FIN
告知对方关闭连接FIN_WAIT2
,这是一种半连接状态。这种状态下,如果对方不完成关闭过程(四次挥手), 会一直保持到系统重启, 过多的FIN_WAIT2
会导致系统内核 crashFIN
报文后,等待对方的ACK
报文的时候,就处于LAST_ACK
状态。当收到对方的ACK报文后,也就可以进入到CLOSED 可用状态了TIME_WAIT
状态下的TCP连接会等待 2*MSL
(Max Segment Lifetime,最大分段生存期,指一个TCP报文在Internet上的最长生存时间。每个具体的TCP协议实现都必须选择一个确定的MSL值,RFC 1122建议是2分钟,但BSD传统实现采用了30秒,Linux可以cat /proc/sys/net/ipv4/tcp_fin_timeout看到本机的这个值),然后即可回到CLOSED 可用状态了。如果FIN_WAIT_1状态下,收到了对方同时带FIN标志和ACK标志的报文时,可以直接进入到TIME_WAIT状态,而无须经过FIN_WAIT_2状态。(这种情况应该就是四次挥手变成三次挥手的那种情况)回顾一下三次握手的过程:
syn
syn+ack
ack
为何是三次而不是两次: 两次握手的时候,客户端收到了服务端的ack,这个时候客户端确认了服务端已经连接成功,但是服务端确不知道客户端是否连接成功,如果客户端没有回复ack ,那么服务端的连接会一直挂载,应该把这个风险放在客户端,让最后一次回复给客户端承担,即使没有连接成功,客户端浪费几个连接也无所谓
回顾四次挥手的过程:
close
, 发送 FIN
给服务端,告知要断开FIN
, 发送ack
给客户端FIN
给客户端,通知关闭客户端FIN
,回复ack
, 连接关闭为什么是四次挥手:
当服务端第一次收到FIN
时,仅仅代表客户端没有报文传输了,但是服务端的报文不一定全部传输给了客户端,所以当服务端的报文传输完成后,再次发送FIN
,告知客户端同意关闭。 主要的目的是确保报文能够完整传输
netstat -nat
: 查看 tcp 各个连接状态的数量tcpdump -iany tcp port 9000
: 对tcp端口为9000的进行抓包lsof -i:9000
, 对9000端口的套接字状态进行查看