最近机房变迁,运维部署新的生产环境发现了老项目的一个bug:该项目所在服务器的tcp连接中为CLOSE_WAIT状态的连接数量很多(会占用大量资源)。提醒开发需要注意是否是代码的问题,检查代码发现确实在某些地方发起http请求之后连接未关闭,处理之后恢复了正常。
那么什么是CLOSE_WAIT状态的连接呢?
首先说一说TCP协议中,断开连接的流程,引用《网络是怎样连接的》这本书中的描述:
和服务器的通信结束之后,用来通信的套接字也就不会再使用了,这 时我们就可以删除这个套接字了。不过,套接字并不会立即被删除,而是 会等待一段时间之后再被删除。 等待这段时间是为了防止误操作,引发误操作的原因有很多,这里无 法全部列举,下面来举一个最容易理解的例子。假设和图 2.12 的过程相 反,客户端先发起断开,则断开的操作顺序如下。
- 客户端发送 FIN
- 服务器返回 ACK 号
- 服务器发送 FIN
- 客户端返回 ACK 号
如果最后客户端返回的 ACK 号丢失了,结果会如何呢?这时,服务 器没有接收到 ACK 号,可能会重发一次 FIN。如果这时客户端的套接字已 经删除了,会发生什么事呢?套接字被删除,那么套接字中保存的控制信 息也就跟着消失了,套接字对应的端口号就会被释放出来。这时,如果别 的应用程序要创建套接字,新套接字碰巧又被分配了同一个端口号 B,而服 务器重发的 FIN 正好到达,会怎么样呢?本来这个 FIN 是要发给刚刚删除 A 如果接收缓冲区中还有剩余的已接收数据,则这些数据会被传递给应用程序。 B 客户端的端口号是从空闲的端口号中随意选择的。 2.4.2 删除套接字 2.4 从服务器断开并删除套接字 的那个套接字的,但新套接字具有相同的端口号,于是这个 FIN 就会错误 地跑到新套接字里面,新套接字就开始执行断开操作了。之所以不马上删 除套接字,就是为了防止这样的误操作。 至于具体等待多长时间,这和包重传的操作方式有关。网络包丢失之后 会进行重传,这个操作通常要持续几分钟。如果重传了几分钟之后依然无 效,则停止重传。在这段时间内,网络中可能存在重传的包,也就有可能发 生前面讲到的这种误操作,因此需要等待到重传完全结束。协议中对于这 个等待时间没有明确的规定,一般来说会等待几分钟之后再删除套接字。
具体我们关注的是这4步断开操作,也就是常说的4次挥手。
- 客户端发送 FIN
- 服务器返回 ACK 号
- 服务器发送 FIN
- 客户端返回 ACK 号
这4步详细的来说,应该是如下:
- 客户端发送一个FIN,用来关闭客户端到服务器的数据传送,此后客户端进入FIN_WAIT_1状态。
- 服务器收到FIN后,进入CLOSE_WAIT状态。正常情况下会发送一个ACK给客户端,确认序号为收到序号+1(与SYN相同,一个FIN占用一个序号),
- 服务器发送一个FIN,用来关闭服务器到客户端的数据传送,服务器进入LAST_ACK状态。
- 客户端收到FIN后,客户端进入TIME_WAIT状态,接着发送一个ACK给服务器,确认序号为收到序号+1,服务器进入CLOSED状态,完成四次挥手。
也就是说,第二步进入了CLOSE_WAIT状态,而服务器中CLOSE_WAIT状态过多说明服务器没有返回ack给客户端。检查代码发现某些地方使用完连接之后没有释放连接,修改代码之后,恢复了正常,连接进入TIME_WAIT状态,随后被系统释放。
更多的关于TCP相关介绍可以参考下面的博文:
理解TCP/IP三次握手与四次挥手的正确姿势 - 吉米乐享驿站 - 博客园www.cnblogs.com![8209b88c16331d7cccddfe58ec2fcc4b.png](https://img-blog.csdnimg.cn/img_convert/8209b88c16331d7cccddfe58ec2fcc4b.png)
![3d71873981bcbef1f7206ea86f4d5d11.png](https://img-blog.csdnimg.cn/img_convert/3d71873981bcbef1f7206ea86f4d5d11.png)