【问题解析】Socket已经关闭了,但是端口还处于listening状态?

最近在上手一个QT项目的时候,在windows下用C++实现了一个服务器功能,开启50000-50008端口进行TCP监听,用来和另一个工具进行交互,服务器要求能动态创建和删除。但是在实际测试的时候发现我的服务器有的时候删不掉,即使socket已经关闭了,但是端口还是处于LISTENING的状态。

最开始的时候我的排查思路一直在线程资源没有回收掉上,下断点发现线程确实已经退出了,socket也关闭掉了,处于无效值状态了。也尝试在套接字的选项上进行了一些修改,结果都无济于事。我甚至一度开始怀疑windows下socket使用上是不是有什么隐藏的机制。

由于这个问题的出现很有规律,是客户端再向我的服务器的50001-50008端口发送消息之后,才会导致所有端口关不掉,我就将关闭服务器的代码不断地切换位置,想确认一下是到哪个位置才关闭不掉了。发现是客户端向我的服务器发送消息之后,我的服务器会开启tftpd32进程来准备传输数据,而我是通过子进程的方式创建的tftpd32进程,瞬间恍然大悟了。我将创建的方式修改为了单独进程的方式,果然可以顺利关闭掉端口了。

那整个问题就变成了 "为什么我开了服务器之后,创建一个子进程,这时候咋在主进程里面就关闭不掉服务器了?"

核心根因:Windows套接字内核引用共享机制

Windows 系统中,Socket 套接字本质是内核对象,并不是单纯的进程私有资源。 当在主进程创建 TCP 监听 Socket(50000~50008)后,通过子进程方式启动 tftpd32 时:

  1. Windows 默认继承父进程所有内核句柄(Socket、文件句柄、网络句柄等);

  2. TCP 监听 Socket 句柄,被 tftpd32 子进程隐性继承;

  3. 此时,同一个监听端口的 Socket 内核对象,同时存在两个进程引用:主进程 和 tftpd32子进程

为什么关 Socket、关线程都没用?

  1. 在主进程中:正常关闭 Socket 句柄、销毁服务对象、退出监听线程; 仅仅只关闭了「主进程」的 Socket 引用;

  2. 但 tftpd32 子进程依然持有该 Socket 内核句柄;

  3. Windows 端口释放机制:只有占用端口的所有进程,全部关闭 Socket 句柄,端口才会解除 LISTENING 状态; 只要还有任意一个进程持有该监听 Socket 句柄,内核就会保留端口监听,端口永久占用、无法释放。 这就是看到的现象: 主进程 socket 已关闭、线程已销毁、变量置空,但端口依然挂在 LISTENING,删不掉服务。

为什么「改为独立进程」就正常了?

我修改了进程启动方式,脱离子进程继承机制、以独立进程启动 tftpd32:

  • 关闭了句柄继承特性;
  • tftpd32 进程不会继承父进程的 TCP 监听 Socket 句柄;
  • 主进程关闭 Socket 时,该内核对象唯一引用被释放;
  • 内核正常回收端口, LISTENING 状态解除,服务可正常销毁。

补充:QT / Windows 解决方案

  1. 创建子进程时,禁用句柄继承 Windows 创建子进程时,设置句柄不可继承,从根源杜绝;

  2. 临时屏蔽网络句柄继承

  3. 独立进程启动第三方工具

总结

Windows 下 Socket 是内核句柄,子进程默认继承父进程所有网络句柄;tftpd32 子进程偷占了监听端口 Socket,主进程关句柄没用,端口被内核锁定,改为独立进程、关闭句柄继承即可彻底解决。

相关推荐
李游Leo11 小时前
HarmonyOS 7 + Spatial Recon Kit-C++:3DGS 高斯参数的非有限值隔离与可渲染性门禁【鸿蒙心迹】
开发语言·c++·3d·harmonyos
忆挽篱笙歌11 小时前
gcc,g++
linux
时间的拾荒人12 小时前
Qt 文件操作详解:从基础类到读写实战
开发语言·qt·面试
jimy112 小时前
基类没有虚析构函数,基类指针删除派生类对象,行为是未定义的
开发语言·c++
l1t12 小时前
修复WSL CreateInstance/E_UNEXPECTED和 mounted read-only 错误
linux·windows·wsl
Ruiery12 小时前
Linux 6.6内核 CPU 深度解析(九):时钟与 TSC — 内核怎么从 PIT/HPET 切到 TSC
linux·运维·服务器
xlq2232213 小时前
全面复习2
c++
无名猿13 小时前
移动构造与移动赋值:把资源偷过来
c++·性能优化·内存管理·现代c++·语法基础
打工仔折腾 AI13 小时前
用UU远程把家里电脑变成AI Agent常驻服务器:CLI、端口映射与代理实测
运维·服务器·人工智能·后端·python·电脑·ai agent 实战
91刘仁德13 小时前
Linux网络编程从入门到实战:UDP/TCP协议与socket编程全解析
linux·网络·笔记·tcp/ip·udp