linux端口被占用,netstat查看无进程号,端口状态一直停留在FIN_WAIT1以及CLOSE_WAIT状态

环境信息:

CentOS 6.5

现象:

同事启动程序发现端口被占用,netstat查看之后发现如下现象:

发现端口处于FIN_WAIT1状态以及CLOSE_WAIT状态,无法释放

问题分析:

FIN_WAIT1以及CLOSE_WAIT状态的原理以及产生的原因大家自行baidu,下面就说下怎么解决上述问题,释放端口

FIN_WAIT1:

1、sysctl -a |grep tcp_max_orph(记下 net.ipv4.tcp_max_orphans  的值  第三步需要赋给orig_orphans)  

2、sysctl -w net.ipv4.tcp_max_orphans=0   然后等待FIN_WAIT1的消失,可以用 netstat -np|grep 9080  反复查看,直到没有任何条目

3、sysctl -w net.ipv4.tcp_max_orphans=$orig_orphans

执行完毕查看:

CLOSE_WAIT:

当客户端因为某种原因先于服务端发出了FIN信号,就会导致服务端被动关闭,若服务端不主动关闭socket发FIN给Client,此时服务端Socket会处于CLOSE_WAIT状态(而不是LAST_ACK状态)。通常来说,一个CLOSE_WAIT会维持至少2个小时的时间(系统默认超时时间的是7200秒,也就是2小时)。如果服务端程序因某个原因导致系统造成一堆CLOSE_WAIT消耗资源,那么通常是等不到释放那一刻,系统就已崩溃。因此,解决这个问题的方法还可以通过修改TCP/IP的参数来缩短这个时间,于是修改tcp_keepalive_*系列参数: 
tcp_keepalive_time: 
/proc/sys/net/ipv4/tcp_keepalive_time 
INTEGER,默认值是7200(2小时) 
当keepalive打开的情况下,TCP发送keepalive消息的频率。建议修改值为1800秒。 

tcp_keepalive_probes:INTEGER 
/proc/sys/net/ipv4/tcp_keepalive_probes 
INTEGER,默认值是9 
TCP发送keepalive探测以确定该连接已经断开的次数。(注意:保持连接仅在SO_KEEPALIVE套接字选项被打开是才发送.次数默认不需要修改,当然根据情形也可以适当地缩短此值.设置为5比较合适) 

tcp_keepalive_intvl:INTEGER 
/proc/sys/net/ipv4/tcp_keepalive_intvl 
INTEGER,默认值为75 
当探测没有确认时,重新发送探测的频度。探测消息发送的频率(在认定连接失效之前,发送多少个TCP的keepalive探测包)。乘以tcp_keepalive_probes就得到对于从开始探测以来没有响应的连接杀除的时间。默认值为75秒,也就是没有活动的连接将在大约11分钟以后将被丢弃。(对于普通应用来说,这个值有一些偏大,可以根据需要改小.特别是web类服务器需要改小该值,15是个比较合适的值) 

 

修改方法:

一、 命令修改:(暂时生效,重新启动服务器后,会还原成默认值) 
sysctl -w net.ipv4.tcp_keepalive_time=600   
sysctl -w net.ipv4.tcp_keepalive_probes=2 
sysctl -w net.ipv4.tcp_keepalive_intvl=2 


注意:Linux的内核参数调整的是否合理要注意观察,看业务高峰时候效果如何。 

二、 若做如上修改后,可起作用;则做如下修改以便永久生效。 
vi /etc/sysctl.conf 

若配置文件中不存在如下信息,则添加: 
net.ipv4.tcp_keepalive_time = 1800 
net.ipv4.tcp_keepalive_probes = 3 
net.ipv4.tcp_keepalive_intvl = 15 


编辑完 /etc/sysctl.conf,要重启network 才会生效 
/etc/rc.d/init.d/network restart 
然后,执行sysctl命令使修改生效,基本上就算完成了。

问题总结:

       上述操作完事后,常规的问题应该就能解决了,但是在我的场景中未能解决,CLOSE_WAIT进程未能关闭,因为netstat命令中第二列的REC-Q也就是接收队列不为0,有积压,导致CLOSE_WAIT未能被自动关闭,经排查,发现该端口对应的进程都成为了僵尸进程导致端口无法释放。

      僵尸进程又分为僵尸进程和孤儿进程,区别是通过ps命令查看僵尸进程的PPID是否为空,不为空是僵尸进程,为空是孤儿进程。如果是僵尸进程,处理方式参照我之前的博客https://blog.youkuaiyun.com/microGP/article/details/81509335;如果是孤儿进程,一般情况下操作系统在一定时间内自己就回收了,稍等即可

<think>好的,用户现在的问题是关于之前的TCP连接断开问题,但这次是服务端卡在FIN_WAIT1状态无法退出。需要详细分析原因和解决方案。 首先,我得回忆一下TCP关闭连接的过程。正常情况是四次挥手:主动关闭方发送FIN,进入FIN_WAIT1,然后收到对方的ACK后进入FIN_WAIT2,再收到对方的FIN后发送ACK,进入TIME_WAIT。如果服务端卡在FIN_WAIT1,说明它发送了FIN没有收到对方的ACK,或者对方没有发送FIN。 接下来,用户提到是服务端卡在这个状态,所以可能服务端是主动关闭连接的一方。比如,服务端先调用了close()。这时候,服务端发送FIN,等待ACK,但一直没收到,导致停留FIN_WAIT1。 可能的原因有几个: 1. 对方没有回复ACK:可能客户端崩溃、网络问题或者客户端没有正确处理关闭流程,导致ACK丢失或未发送。 2. 对方的ACK丢失:服务端发送FIN后,客户端回了ACK,但ACK在网络中丢失,服务端没收到,一直等待。 3. 客户端没有发送FIN:如果是服务端主动关闭,客户端在收到FIN后应该发送ACK,然后客户端进入CLOSE_WAIT状态,并继续处理数据,最后发送自己的FIN。如果客户端没有发送FIN,服务端可能一直等待,不过正常情况下,服务端在收到ACK后会进入FIN_WAIT2,而不是一直卡在FIN_WAIT1,所以可能问题出在ACK未到达。 需要再仔细分析状态转换。FIN_WAIT1状态会持续直到收到ACK,如果没收到,会超时重传FIN。根据系统配置,重试次数和超时时间可能不同,但最终会关闭连接。如果服务端一直卡住,可能是对方一直没响应,或者系统配置问题导致重试次数过多,或者资源泄漏。 解决方案方面: - 检查客户端是否正常响应ACK。可能需要客户端正确处理关闭,确保发送ACK。 - 设置SO_LINGER选项,调整关闭行为,避免长时间等待。 - 调整内核参数,比如缩短FIN超时时间,或者增加重试次数。 - 确保服务端正确处理异常情况,比如强制关闭socket,释放资源。 - 网络问题排查,确保ACK能够到达服务端。 另外,用户可能遇到的情况是,当Wi-Fi断开时,客户端突然下线,导致无法发送ACK,服务端卡在FIN_WAIT1。此时可能需要应用层的心跳机制,或者设置超时,及时释放资源。 需要给出具体的解决步骤,比如代码示例如何设置SO_LINGER,调整内核参数的方法,以及如何处理客户端异常断开的情况。同时,用户可能需要工具来监控TCP状态,比如使用netstat或者adb命令查看连接状态,帮助诊断问题。</think>当服务端在关闭TCP连接时卡在`FIN_WAIT1`状态无法退出,通常是由于TCP连接的**被动关闭方未正确响应ACK**或**网络异常导致ACK丢失**。以下是详细分析和解决方案: --- ### 一、`FIN_WAIT1`状态机制 1. **TCP四次挥手流程** - 主动关闭方(服务端)发送`FIN`报文,进入`FIN_WAIT1`状态; - 被动关闭方(客户端)收到`FIN`后,回复`ACK`,服务端进入`FIN_WAIT2`; - 客户端发送自己的`FIN`,服务端回复`ACK`后进入`TIME_WAIT`,最终关闭连接。 2. **卡在`FIN_WAIT1`的原因** - **客户端未回复`ACK`**:客户端崩溃、未正确处理关闭逻辑,或网络丢包导致`ACK`未送达; - **客户端未发送`FIN`**:客户端未完全关闭连接(如未调用`close()`); - **服务端配置问题**:系统内核参数限制(如`tcp_fin_timeout`过短)。 --- ### 二、问题排查步骤 #### 1. 确认网络连通性 - 使用`ping`或`tcpdump`检查客户端与服务端之间的网络是否通畅; - 通过`adb shell netstat -anp | grep <端口>`(Android)或`netstat -ano`(PC)查看连接状态。 #### 2. 检查客户端行为 - 客户端是否正常调用`close()`释放Socket; - 客户端是否因线程阻塞、异常未处理导致未响应`ACK`。 #### 3. 分析服务端代码 - 服务端关闭Socket时是否调用了`shutdownOutput()`和`close()`; - 是否存在未关闭的输入/输出流导致资源泄漏。 --- ### 三、解决方案 #### 1. 客户端强制确认`ACK` 确保客户端收到`FIN`后立即回复`ACK`: ```java // 客户端代码示例 try { socket.shutdownInput(); // 关闭输入流,触发ACK socket.close(); } catch (IOException e) { e.printStackTrace(); } ``` #### 2. 服务端设置`SO_LINGER`选项 通过`SO_LINGER`强制缩短关闭等待时间: ```java // 服务端代码示例(Java) Socket socket = serverSocket.accept(); socket.setSoLinger(true, 0); // 第二个参数为超时时间(秒),0表示直接丢弃未发送数据 ``` #### 3. 调整系统内核参数(需Root权限) 修改`tcp_fin_timeout`缩短`FIN_WAIT1`超时(默认值因系统而异): ```bash # Android通过adb修改(临时生效) adb shell "echo 10 > /proc/sys/net/ipv4/tcp_fin_timeout" ``` #### 4. 服务端容错处理 添加超时机制,避免无限等待`ACK`: ```java // 服务端Socket设置超时 socket.setSoTimeout(5000); // 5秒超时 try { // 读取数据或等待ACK } catch (SocketTimeoutException e) { socket.close(); // 超时后强制关闭 } ``` #### 5. 心跳保活机制 通过应用层心跳检测,及时清理僵死连接: ```java // 服务端定期发送心跳包 while (true) { try { socket.getOutputStream().write("HEARTBEAT\n".getBytes()); Thread.sleep(30000); // 每30秒一次 } catch (Exception e) { socket.close(); // 异常时主动关闭 break; } } ``` --- ### 四、进阶优化 1. **使用连接池** 复用长连接,避免频繁创建/销毁Socket(如OkHttp连接池)。 2. **监控TCP状态** 通过`ss -t4 state <状态名>`(Linux)或第三方库(如Netty的`ChannelTracker`)实时监控连接状态。 3. **防御性代码设计** - 在`finally`块中确保关闭Socket; - 使用`try-with-resources`(Java 7+)自动释放资源: ```java try (Socket socket = serverSocket.accept(); InputStream is = socket.getInputStream()) { // 业务逻辑 } catch (IOException e) { e.printStackTrace(); } ``` --- ### 五、验证方法 1. **抓包分析** 使用Wireshark或`tcpdump`验证`FIN`和`ACK`的交互流程: ```bash adb shell tcpdump -i wlan0 -s0 -v 'port <端口>' ``` 2. **日志追踪** 在服务端和客户端添加关键日志: ```java Log.d("SocketClose", "FIN sent, entering FIN_WAIT1"); Log.d("SocketClose", "ACK received, entering FIN_WAIT2"); ``` 3. **压力测试** 模拟网络抖动、客户端崩溃等场景,验证服务端是否能正常回收资源。 --- 通过以上方法,可有效解决服务端因`FIN_WAIT1`状态卡顿导致的端口占用问题,同时提升TCP连接的健壮性。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值