40

我正在为 Linux 编写一个 Unix 域套接字服务器。

我很快发现 Unix 域套接字的一个特点是,在创建侦听 Unix 套接字时会创建匹配的文件系统条目,关闭套接字并不会删除它。此外,在手动删除文件系统条目之前,不可能再次bind()将套接字连接到同一路径:如果给出的路径已经存在于文件系统中,则会bind()失败。EADDRINUSE

因此,套接字的文件系统条目需要unlink()在服务器关闭时被 'ed 以避免EADDRINUSE服务器重新启动。然而,这不能总是做到(即:服务器崩溃)。我发现的大多数常见问题解答、论坛帖子、问答网站都只是unlink()在调用bind(). 然而,在这种情况下,最好在unlink()'ing 之前知道一个进程是否绑定到这个套接字。

实际上,unlink()在进程仍绑定到 Unix 套接字时,然后重新创建侦听套接字不会引发任何错误。然而,结果是旧的服务器进程仍在运行但无法访问:旧的侦听套接字被新的“屏蔽”。必须避免这种行为。

理想情况下,使用 Unix 域套接字,套接字 API 应该具有与绑定 TCP 或 UDP 套接字时相同的“互斥”行为:“我想将套接字 S 绑定到地址 A;如果进程已经绑定到此地址,只是抱怨! ”不幸的是,事实并非如此......

有没有办法强制执行这种“互斥”行为?或者,给定一个文件系统路径,有没有办法通过套接字 API 知道系统上的任何进程是否有一个绑定到该路径的 Unix 域套接字?我应该使用套接字 API ( flock(), ...) 外部的同步原语吗?还是我错过了什么?

感谢您的建议。

注意:Linux 的抽象命名空间 Unix 套接字似乎解决了这个问题,因为unlink(). 但是,我正在编写的服务器旨在通用:它必须对两种类型的 Unix 域套接字都具有鲁棒性,因为我不负责选择侦听地址。

4

2 回答 2

25

我知道我参加聚会很晚了,而且很久以前就已经回答了,但我只是在寻找其他东西时遇到了这个问题,我有一个替代建议。

当您遇到EADDRINUSE返回时,bind()您可以输入连接到套接字的错误检查例程。如果连接成功,则有一个正在运行的进程至少还活着,可以完成accept(). 这让我觉得这是实现您想要实现的目标的最简单和最便携的方式。它的缺点在于,最初创建 UDS 的服务器实际上可能仍在运行,但以某种方式“卡住”并且无法执行accept(),因此该解决方案当然不是万无一失的,但它是正确的一步我认为的方向。

如果connect()失败则继续和unlink()端点并重试bind()

于 2012-12-05T09:25:33.120 回答
11

除了你已经考虑过的事情之外,我认为没有什么可以做的。你似乎研究得很好。

有一些方法可以确定套接字是否绑定到 unix 套接字(显然 lsof 和 netstat 会这样做),但它们很复杂并且依赖于系统,以至于我怀疑它们是否值得努力处理您提出的问题。

您确实提出了两个问题 - 处理与其他应用程序的名称冲突以及处理您自己的应用程序的先前实例。

根据定义,您的 pgm 的多个实例不应尝试绑定到同一路径,因此这可能意味着您一次只希望一个实例运行。如果是这种情况,您可以只使用标准的 pid 文件锁定技术,这样两个实例就不会同时运行。如果您无法获得锁,您不应该取消现有套接字的链接,甚至不应该运行。这也处理了服务器崩溃的情况。如果你能得到锁,那么你知道你可以在绑定之前取消链接现有的套接字路径。

AFAIK 无法控制其他程序创建碰撞。文件权限并不完美,但如果您可以使用该选项,您可以将您的应用程序放在自己的用户/组中。如果存在现有的套接字路径并且您不拥有它,则不要取消链接并发出错误消息并让用户或系统管理员对其进行排序。使用配置文件使其易于更改 - 并可供客户使用 - 可能会起作用。除此之外,您几乎必须使用某种发现服务,除非这是一个非常关键的应用程序,否则这似乎是巨大的矫枉过正。

总的来说,您可以放心,这实际上并不经常发生。

于 2011-09-14T02:08:27.567 回答