windows下进程退出端口仍在

本文介绍了一种常见的情况,即启动Socket服务时遇到端口被占用的问题,并详细解释了问题产生的原因。通常是因为父进程拉起子进程时设置了句柄继承,导致服务重启时端口仍被占用。文章给出了具体的解决办法,通过修改CreateProcess中的参数或者在创建Socket后调用SetHandleInformation来禁用句柄继承。

现象:拉起进程创建Socket服务时出错,错误原因是端口被占用,OK,这个简单,你使用

netstat -ano | find "端口号"

终于,你发现某一个PID的进程占用了这个端口,心想,找到对应的PID的进程杀掉即可,但是当你按上面提示的PID到设备管理器里面去找时却发现没有对应PID的进程在运行,怎么回事?

一般这种情况都是起Socket端口的进程又拉起了子进程,且在拉起子进程的时候直接或间接调用CreateProcess时候继承设置为TRUE,然后服务进程退出了,子进程没有退出,再启服务进程的时候就会报端口被占用。

BOOL WINAPI CreateProcess(
  _In_opt_    LPCTSTR               lpApplicationName,
  _Inout_opt_ LPTSTR                lpCommandLine,
  _In_opt_    LPSECURITY_ATTRIBUTES lpProcessAttributes,
  _In_opt_    LPSECURITY_ATTRIBUTES lpThreadAttributes,
  _In_        BOOL                  bInheritHandles,   // 此处设置了TRUE
  _In_        DWORD                 dwCreationFlags,
  _In_opt_    LPVOID                lpEnvironment,
  _In_opt_    LPCTSTR               lpCurrentDirectory,
  _In_        LPSTARTUPINFO         lpStartupInfo,
  _Out_       LPPROCESS_INFORMATION lpProcessInformation
);

当然你可以把上面的TRUE改为FALSE,但有时候可能这个参数需要设置为TRUE,怎么办呢?解决方法也很简单,在创建Socket时,bind端口之后,调用SetHandleInformation

// 关闭Socket句柄继承
SetHandleInformation((HANDLE)socket, HANDLE_FLAG_INHERIT, 0 );

问题解决,以后服务进程退出了端口也就会被释放。

Windows 系统中,进程退出Socket 仍然处于监听状态的现象通常与资源释放机制和 Socket 的配置有关。当一个服务端程序异常终止或正常关闭时,操作系统应当回收该进程所占用的网络资源,包括监听的端口和连接的 Socket。然而,在某些情况下,这些资源可能未能及时释放,导致新的实例无法绑定到相同的端口。 ### 原因分析 1. **Socket 未正确关闭**:如果进程退出前没有显式调用 `closesocket` 来关闭所有打开的 Socket,操作系统可能不会立即释放相关的资源。这种情况下,即使进程已经结束,系统仍可能保留部分状态信息,尤其是在使用了异步 I/O 或者重叠操作的情况下[^2]。 2. **TIME_WAIT 状态**:TCP 协议规定,在连接关闭之后,Socket 会进入 TIME_WAIT 状态,持续时间为 2MSL(Maximum Segment Lifetime),通常为 2 分钟。这是为了确保网络上残留的数据包能够被丢弃,防止它们干扰新建立的连接。因此,即使服务端进程已经终止,端口也可能因为仍有连接处于 TIME_WAIT 状态而被认为“仍在使用”[^1]。 3. **SO_REUSEADDR 选项未启用**:Windows 和 Linux 一样,提供了 SO_REUSEADDR 选项来允许新的 Socket 绑定到一个已经存在的地址上,即使该地址仍然处于 TIME_WAIT 状态。如果没有设置这个选项,则新的绑定请求将失败并返回错误代码 WSAEADDRINUSE(10048)。 4. **子线程或异步操作未完成**:如果主进程创建了额外的线程用于处理客户端连接,或者使用了异步通知机制(如 WSAAsyncSelect),那么即使主线程已经退出,这些后台操作可能还在运行。只要还有线程在访问 Socket,系统就不会完全释放该资源[^5]。 ### 解决方案 1. **确保正确关闭所有 Socket**:在进程退出之前,应遍历所有打开的 Socket 并调用 `closesocket` 函数。此外,还应该检查是否有任何挂起的操作需要取消,例如通过调用 `WSACancelBlockingCall` 来中断阻塞调用,或者使用 `CancelIoEx` 取消正在进行的 I/O 请求[^4]。 2. **启用 SO_REUSEADDR 选项**:为了避免因 TIME_WAIT 状态而导致的问题,可以在创建监听 Socket 后立即设置 SO_REUSEADDR 标志。这可以通过调用 `setsockopt` 函数实现,如下所示: ```cpp BOOL bReuseAddr = TRUE; if (setsockopt(s, SOL_SOCKET, SO_REUSEADDR, (const char*)&bReuseAddr, sizeof(BOOL)) == SOCKET_ERROR) { // 处理错误 } ``` 设置此选项后,新的 Socket 就可以成功绑定到同一个地址,即便之前的连接仍处于 TIME_WAIT 状态[^1]。 3. **等待所有子线程结束**:对于创建了多个线程的应用程序来说,必须确保所有子线程都已经安全退出后再关闭主进程。可以使用 `WaitForMultipleObjects` 或者类似函数等待所有线程句柄变为有信号状态,然后再继续执行清理工作[^5]。 4. **使用 `shutdown` 函数优雅地关闭连接**:除了直接调用 `closesocket` 外,还可以先调用 `shutdown` 函数以只读、只写或双向方式关闭连接。这样做的好处是可以保证数据传输的完整性,同时也能加快资源释放的速度[^3]。 5. **检查注册表设置调整 TCP/IP 参数**:作为最后手段,也可以考虑修改 Windows 注册表中的 TCP/IP 参数,比如减少 `TcpTimedWaitDelay` 的值,从而缩短 TIME_WAIT 的持续时间。但这种方法风险较高,建议仅在其他方法无效时采用,并且操作前务必备份注册表。 综上所述,解决 Windows 进程退出Socket 仍然在监听的问题需要从多个方面入手,包括但不限于正确的资源管理、合理配置 Socket 选项以及妥善处理多线程环境下的同步问题。
评论 1
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值