【问题标题】:UNIX domain socket not closed after close()UNIX 域套接字在 close() 后未关闭
【发布时间】:2015-02-18 18:52:49
【问题描述】:

我有一个客户端应用程序通过 QMP Unix 域套接字与 QEMU 进程通信。有时,客户端在套接字连接上调用 close() 后,“netstat -ap unix”仍将其显示为 CONNECTED 状态。我确实检查了 close() 调用的返回值,它成功返回值为 0,但连接似乎仍然挥之不去。

由于 QMP 并不真正支持其套接字上的多个连接,因此所有后续连接到套接字的调用都会失败,因为它们会无限期地等待延迟连接被关闭。

有没有办法从代码中确定套接字真的关闭了,有没有办法强制套接字关闭?

【问题讨论】:

  • 听上去QEMU服务器进程还没有完全读完最后一个send()的内容。如果您切换到使用 TCP 而不是域套接字会发生什么?行为有什么不同吗?
  • 我刚才描述的行为不是 100% 可重现的,不幸的是,使用 TCP 对我来说不是一个选项。
  • 我们能看到netstat -ap的输出吗?
  • 我会冒险猜测它是套接字缓冲区中的待处理内容,因此从套接字读取可能会关闭它。
  • 检查您是否没有通过fork()/exec() 或类似方法将 fd 泄漏到另一个进程中。使用fcntl(fd, F_SETFD, FD_CLOEXEC) 确保这不是问题的根源。

标签: c sockets unix virtualization qemu


【解决方案1】:

可能是文件描述符已被duped、forked 或泄露。

closeing 之前调用shutdown(sock, SHUT_RDWR) 以确保关闭连接。

【讨论】:

  • 当我尝试连接到一个新的套接字时,如果另一个连接仍然存在,我会得到 -EAGAIN。在我使用shutdown()之后,我实际上可以在没有得到-EAGAIN的情况下连接()到套接字,即使另一个套接字不是close()d
【解决方案2】:

您是否尝试过从另一端关闭套接字?它是异步的,但它让双方都有机会确保套接字关闭。

您可以向另一端的侦听器发送关闭命令并让它回收套接字。当套接字关闭时,您应该最终得到一个 SIGPIPE。抓住 SIGPIPE 并关闭套接字的末端。如果你最终得到一个 EPIPE 这样做,那么忽略它。这只是意味着您已经收到关于套接字关闭的通知。

【讨论】:

    【解决方案3】:

    您可以通过 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) 的调用将启动通信链接的中止,丢弃所有待处理的数据并立即关闭套接字。

    【讨论】:

    • 它不会“向呼叫者保证两端都已确认正常关机”。它只向调用者保证所有待处理的数据都已发送。
    • 有问题的套接字是一个基于文件的 UNIX 域套接字,所以我不确定使用 SO_LINGER 是否会有所帮助。
    猜你喜欢
    • 2014-03-06
    • 2017-04-08
    • 2012-09-25
    • 1970-01-01
    • 1970-01-01
    • 2010-11-02
    • 2014-06-02
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多