0X01
正常状况下TCP链接会经过4次挥手进行拆链(也有经过RST拆除链接的可能,见为何服务器忽然回复RST——当心网络中的安全设备),下图TCP状态机展现了TCP链接的状态变化过程:html

咱们重点看4次挥手的过程:后端
- 想要拆除链接的一方A发送FIN报文,自身进入到FIN_WAIT_1状态;
- 被拆除链接的一方B接收到FIN报文,发ACK,自身进入到CLOSE_WAIT状态;
- A收到ACK,进入FIN_WAIT_2状态;
- B发送FIN,自身进入LAST_ACK状态;
- A收到FIN,发送ACK,自身进入TIME_WAIT状态;
- B收到ACK报文,B上的这个socket关闭,端口释放;
- A等待2MSL后socket关闭,释放端口。
从以上链接拆除过程咱们能够看到:主动发送第一个FIN报文的一方会进入TIME_WAIT状态;进入TIME_WAIT状态的一方须要等待2MSL时间才会释放端口,在2MSL时间内,这个socket对应的四元组(源目IP、源目端口)处于冻结状态。安全
TIME_WAIT状态的做用主要有两个:服务器
- 避免拆链报文在链路中丢失形成链接关闭异常:在第6步,B没有收到ACK报文的时候会认为A没有收到FIN包,进而会重传第4步的FIN,若是这个时候没有TIME_WAIT状态,A侧socket已经关闭,A会针对B发送的FIN包响应RST,有可能致使B链接异常。
- 避免乱序到来的业务报文在新生成的socket链接中引起混乱:假设在拆链前有TCP报文因为中间网络传输缘由致使在第7步完成以后才到达,若是没有TIME_WAIT状态而A和B又使用一样的4元组新建了一个新的socket,那么迷路的数据包就会进入到新的socket中进行处理,可能致使业务异常。
经过TIME_WAIT状态能够很好的规避上面提到的两个问题,TIME_WAIT状态的老化时间是2MSL,MSL是最大分段生存时间,表示的是一个TCP分段可能在网络上存在的最大时间。2倍MSL的设计能够很好的知足报文在A、B之间一来一回的最大须要消耗的时间,最大程度上避免上述两个问题。在CentOS系统中,MSL的时间通常是30S。网络
0X02
下图抓包截图展现了一个完整的链接拆除又复用一样的端口新建链接的过程。socket

在图中server 192.168.221.1运行Web服务,监听82端口,client 192.168.252.2 使用31387端口链接server(抓包截图从挥手前开始截取)。能够看到在编号为3的报文中服务器主动拆除链接,服务器和客户端交互完4个完整的挥手报文后,客户端当即使用相同的源端口和服务器的监听端口创建新的链接。下面逐报文对整个交互过程进行分析:性能
- server→client(PSH、ACK):服务器推送数据最后一个分段给客户端
- client→server(ACK):客户端对第1个报文进行接收确认
- server→client(FIN、ACK):服务器发送挥手报文给客户端协商断开链接,这是四次挥手的第一步。同时ACK标志位置位,因为第2个报文中没有载荷数据,因此ack值=第2个报文的seq。此时服务器进入FIN_WAIT_1状态
- client→server(ACK):客户端对接收到的FIN包进行响应,这是四次挥手的第二步。其中seq值不变,ack=第3个报文的seq+1(由于FIN报文在逻辑上占一个长度)。此时客户端进入CLOSE_WAIT状态,服务器收到ACK报文后进入FIN_WAIT_2状态
- client→server(FIN、ACK):客户端给服务器发送FIN包,这是四次挥手的第三步。其seq和ack的值和第4个报文相同。此时客户端进入LAST_ACK状态,等待服务器响应ACK报文后便可关闭链接
- server→client(ACK):服务器收到客户端发送的FIN包后会当即给客户端发送ACK包,这是四次挥手的最后一步。其中seq=第3个报文中的seq+1,ack=第5个报文中的seq+1。客户端收到ACK后会当即close该四元组对应的socket,而此时服务器在发送ACK后会进入TIME_WAIT状态,服务器侧对应的TCP四元组会被冻结2MSL
- client→server(SYN):客户端链接拆除后当即使用同一个源端口31387向服务器的82端口发起新的SYN链接握手报文
- server→client(ACK):经过seq和ack能够看出服务器重传第6个报文。因为服务器对应的四元组仍然在TIME_WAIT状态中,所以对于接受到的报文会认为是迷路的数据包或者客户端没有收到服务器发送的最后一个挥手的ACK报文,因此服务器从新向客户端发送该ACK报文
- client→server(RST):客户端向服务器发送一个RST报文,其中seq为server挥手ack包(第6和第8个报文)的ack值。这是由于对服务器侧而言,对应的四元组仍然处于TIME_WAIT状态,而客户端侧并不存在这个四元组的socket信息,客户端正准备使用这个四元组新建链接。这是前文为何服务器忽然回复RST——当心网络中的安全设备中TCP发送RST的第三种状况:TCP接收到一个数据段,可是这个数据段所标识的链接不存在。因而客户端使用ACK报文中的ack值做为seq,发送RST报文给服务器
能够看到当客户端发送完RST后,客户端再次进行了SYN报文的重传,而这次即便仍然复用以前的四元组,客户端和服务器的TCP三次握手正常创建。这是由于当服务器收到RST报文后,不管处在TCP的哪一个状态,都会当即进入close状态,进而服务器侧对应被TIME_WAIT状态冻结的四元组得以被释放,客户端侧的复用就成功了。设计
0X03
如上所述的业务场景是某应用系统使用反向代理地址链接后端服务器的抓包。服务器主动拆链+客户端当即复用源端口,这是一种危险的实现,若是客户端没有RST或者服务器端识别不了RST则颇有可能在2MSL时间内,客户端使用被冻结的4元组进行链接创建的操做都会失败。对于服务器主动拆链的场景应该保证终端可用源端口尽量的多,尽可能避免当即端口复用的状况。此外对于服务器主动拆链的场景应该尽量调短服务器的MSL时间,避免大量TIME_WAIT状态的链接存在影响服务器性能。代理