关闭close()之后UNIX域套接字未关闭

6
我有一个客户端应用程序,通过QMP Unix域套接字与QEMU进程进行通信。有时,在客户端调用close()关闭套接字连接后,“netstat -ap unix”仍然显示套接字处于连接状态。我确实检查了close()调用的返回值,并且它成功地返回了0,但连接似乎仍在等待。
由于QMP实际上不支持其套接字上的多个连接,因此所有后续连接套接字的调用都会失败,因为它们无限期地等待挂起的连接被关闭。
是否有一种方法可以从代码中确保套接字已真正关闭,是否有一种强制关闭套接字的方法?

1
听起来QEMU服务器进程没有完全读取最后一个send()的内容。如果改用TCP而不是域套接字会发生什么?行为上有什么区别吗? - user590028
1
我猜测这可能是套接字缓冲区中未处理的内容,因此从套接字读取数据可能会关闭它。 - Sobrique
@abligh 我在close()之前不调用shutdown()。通常情况下,数据会从命令中完全读取,直到某个close()操作没有关闭套接字,或者至少我是这么认为的。 - mgamal
在执行 close() 之前调用 shutdown() 不会改变任何东西。显然你漏掉了某个 close() - user207421
2
请检查您是否通过fork()/exec()或类似方式将fd泄漏到另一个进程中。使用fcntl(fd, F_SETFD, FD_CLOEXEC)确保这不是问题的根源。 - Anya Shenanigans
显示剩余9条评论
3个回答

3
可能是文件描述符已经被复制、分叉或泄漏了。在关闭之前,调用 shutdown(sock, SHUT_RDWR) 来确保关闭连接。

以前我尝试连接新套接字时,如果另一个连接仍在进行中,我经常会收到-EAGAIN错误。但是,使用shutdown()之后,即使另一个套接字没有被close(),我也可以成功connect()到该套接字而不会再次收到-EAGAIN错误。 - mgamal

0

你尝试过从另一端关闭套接字吗?这是异步的,但它给双方一个机会确保套接字关闭。

你可以向另一端的监听器发送一个关闭命令,并让它重新启动套接字。当套接字关闭时,你应该会收到一个SIGPIPE信号。捕获SIGPIPE并关闭你的套接字。如果你在这样做时遇到EPIPE,则忽略它。这只是意味着你已经被通知套接字关闭了。


-1

您可以尝试使用setsockopt(2)选项中的SO_LINGER并设置超时时间为0。这样,当您关闭套接字时,它会被强制关闭,发送RST而不是进入FIN/ACK关闭行为。

SO_LINGER选项的目的是在调用close(2)函数时控制套接字如何关闭。此选项仅适用于诸如TCP之类的面向连接的协议。

内核的默认行为是允许close(2)函数立即返回给调用者。任何未发送的TCP/IP数据将尽可能地传输和交付,但不做任何保证。由于close(2)调用立即返回给调用者,应用程序无法知道最后一位数据是否实际上已经被传送。

可以在套接字上启用SO_LINGER选项,以使应用程序在close(2)调用中阻塞,直到所有最终数据都被传递到远程端点。此外,这确保了调用者两端都确认了正常的套接字关闭。如果失败,则发生指定的选项超时,并向调用应用程序返回错误。

还有一种情况可以应用,即使用不同的SO_LINGER选项值。如果调用应用程序想要立即中止通信,则可以在延迟结构中设置适当的值。然后,调用close(2)将启动通信链路的中止,丢弃所有未决数据并立即关闭套接字。


它并不能“确保呼叫方已确认正常关闭”,它只能确保所有待处理数据已发送给呼叫方。 - user207421
2
所讨论的套接字是基于文件的UNIX域套接字,因此我不确定在这里使用SO_LINGER是否有帮助。 - mgamal

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接