简单总结几个个人觉得较为重要的TCP状态。
名词定义:
- 客户端:发起connect操作的端
- 服务端:发起bind操作的端
- 主动关闭端:主动发起四次挥手端
- 被动关闭端:被动接收四次挥手FIN报文端
CLOSED
- 客户端发送SYN后进入SYN_SENT,若超时未收到ACK,则进入CLOSED
- 被动关闭端接收到FIN后,发送ACK后进入CLOSE_WAIT,等待应用可进入CLOSED状态后,发送FIN后进入LAST_ACK状态,等待并接收到主动关闭端的ACK后进入CLOSED状态
- 主动关闭端接收到FIN后,进入TIME_WAIT,等待2MSL时间后,进入CLOSED状态
- 通过设置SO_LINGER可干预内核对于socket close动作的静默处理
CLOSE_WAIT
若程序有大量socket进入此状态,则意味着被动关闭端大量的连接在收到FIN后,程序没有主动将socket close掉。对应到Java的Mina或Netty框架,应该在IDLE或者EXCEPTION_CAUGHT时,主动将socket进行close。
FIN_WAIT
- FIN_WAIT-1是主动关闭端发送FIN后进入的状态
- 主动关闭端若收到被动关闭端ACK,则进入FIN_WAIT-2
服务端的SYN_RCVD与ESTABLISHED
- 服务端处于SYN_RCVD的Socket存在于服务端的半连接队列中,队列数量配置内核参数tcp_max_syn_backlog。臭名昭著的SYN Flood攻击便是利用TCP服务端的SYN_RCVD状态进行攻击的(服务端静默重发ACK五次),半连接队列满了之后,静默处理是拒绝接受新的SYN,攻击者由此达到了拒绝服务攻击的目的,可通过Linux的SYNcookie防范此攻击(此时tcp_max_syn_backlog值无效)
- 服务端处于ESTABLISHED的Socket存在于服务端的全连接队列,队列数量配置内核参数backlog,队列满了将拒绝接受accept新连接,可配置内核参数设置队列满之后静默丢弃客户端的ACK还是发送回一个RST
TIME_WAIT
如果C端不维持TIME_WAIT状态,而是FIN_WAIT2后直接进入CLOSED状态,那么C端将响应RST分节,S端收到后将此分节解释成一个错误,因此若想实现正常关闭,此环节必不可少;
MSL是一个IP数据报能在互联网上存在的最长时间,而TIME_WAIT持续的时间是两个MSL,这实际上是对路由器异常的容错,防止程序收到脏数据;