【问题标题】:SO_REUSEADDR and AF_UNIXSO_REUSEADDR 和 AF_UNIX
【发布时间】:2013-03-20 22:12:28
【问题描述】:

事实

在 POSIX 文档中,我看不到任何阻止将 SO_REUSEADDR 套接字选项与 AF_UNIX 一起用于 UNIX 域套接字的内容。

但是,如果套接字节点已经存在,它总是在 bind 时间失败,并且似乎被忽略,并且似乎需要在调用 bind 之前首先取消链接文件系统上的套接字节点;简而言之,它不会重用地址。网上有很多关于这个问题的帖子,但没有一个解决方案。

问题

我不会坚持,如果它不起作用,它就不起作用(似乎至少在 BSD 和 Linux 系统上都是一样的),只是有一个问题:这是正常行为吗?是否有任何指针表明它应该被支持,或者相反,任何指针表明它不应该被支持?或者这是未指定的?请注意,该问题是在 POSIX 上下文中提出的,而不是在任何特定平台上下文中。

我欢迎任何关于此问题的 POSIX 参考。

额外:一个小小的 sn-p 不盲目unlink who-know-what

我在网上看到一些帖子,建议unlinkbind 之前的任何预期名称的节点。我觉得这是不安全的,在这种情况下应该只取消链接已经是套接字节点的节点:例如。取消链接名为mysocket 的文本文件以重新创建同名的套接字节点可能是错误的。为此,这里有一个小小的 sn-p:

/* Create the socket node
 * ----------------------
 * Note `SO_REUSEADDR` does not work with `AF_UNIX` sockets,
 * so we will have to unlink the socket node if it already exists,
 * before we bind. For safety, we won't unlink an already existing node
 * which is not a socket node. 
 */

status = stat (path, &st);
if (status == 0) {
   /* A file already exists. Check if this file is a socket node.
    *   * If yes: unlink it.
    *   * If no: treat it as an error condition.
    */
   if ((st.st_mode & S_IFMT) == S_IFSOCK) {
      status = unlink (path);
      if (status != 0) {
         perror ("Error unlinking the socket node");
         exit (1);
      }
   }
   else {
      /* We won't unlink to create a socket in place of who-know-what.
       * Note: don't use `perror` here, as `status == 0` (this is an
       * error we've defined, not an error returned by a system-call).
       */
      fprintf (stderr, "The path already exists and is not a socket node.\n");
      exit (1);
   }
}
else {
   if (errno == ENOENT) {
      /* No file of the same path: do nothing. */
   }
   else {
      perror ("Error stating the socket node path");
      exit (1);
   }
}

/* … invoke `bind` here, which will create the socket node … */

【问题讨论】:

  • 要明确一点:您是要在 POSIX 规范中引用说明这种行为是否正确,还是要问常见的 POSIX 实现的行为方式?
  • 我想知道常见的 POSIX 实现(至少是 Linux 和 BSD)是对还是错;所以是的,我会说我正在寻找类似 POSIX 的引用。注意我知道这可能只是未指定。附:我将在最初的帖子中添加一个我认为有用的小 sn-p。
  • 我认为您应该再添加一个案例。如果文件存在并且是套接字,请尝试connect()。如果失败,则没有服务正在侦听该套接字,因此可以安全地删除它。另外我认为删除不是套接字的文件不应该自动发生。那可能是一个重要的文件!

标签: sockets unix posix


【解决方案1】:

我只能访问一份 POSIX 规范文档,即System Interfaces,所以我将从这里开始尽力而为。

我们的规范探险当然必须从2.10.6 Use of Options 开始,它定义了SO_REUSEADDR 选项如下:

SO_REUSEADDR 选项表示用于验证 bind() 中提供的地址的规则应该允许重用本地地址。此选项的操作是特定于协议的。 SO_REUSEADDR 的默认值为 off;也就是说,不允许重复使用本地地址。

这一段基本上否认了这个选项真正做什么的任何规范,将其委托给底层协议的规范。

Section 2.10.17 Use of Sockets for Local UNIX Connections 描述了创建 UNIX 域套接字的机制,但实际上它唯一告诉我们的是使用什么套接字类型常量以及使用哪个结构来表示地址。 The documentation for the sockaddr_un struct 只告诉我们它的格式,而不是它在bind 上的行为。

The documentation for bind itself 可以理解为与协议无关,它只告诉我们地址已经在使用时会发生什么,而不是告诉我们重新绑定同一个套接字有效的情况。

虽然它不是 POSIX 标准文档,但富士通有 a document on the POSIX sockets API,这很有趣,因为它不是专门针对 Linux 或 BSD 的。本文档的第 2.6.4 节对bind 在 UNIX 套接字上的行为有以下说明:

必须在sun.sun_path 组件中指定的路径名​​在使用bind() 的文件系统中创建为文件。因此,调用bind() 的进程必须对要写入文件的目录具有写入权限。系统不会删除该文件。因此,当不再需要它时,应该由进程将其删除。

虽然这并没有特别提到SO_REUSEADDR,但它确实说明了bind 的行为是创建一个文件,并且绑定过程有责任删除它当它不再被使用时。

最后,这篇文档对setsockopt的描述对SO_REUSEADDR有以下几点要说:

指定对为 bind() 指定的地址进行有效性检查的规​​则应允许重用本地地址如果协议支持这一点

因此,虽然没有具体提及 AF_UNIX,但它确实承认此选项不适用于所有协议。

我也找到了a (non-authoritative) summary describing the intended purpose of SO_REUSEADDR:

这个套接字选项告诉内核,即使这个端口很忙(在TIME_WAIT 状态),继续并重用它。如果它很忙,但处于另一个状态,您仍然会收到地址已在使用中的错误。

TIME_WAIT 状态主要用于面向 Internet 的套接字,以避免新程序绑定到最近被另一个程序使用的端口并无意中接收与旧程序相关的数据包。这个问题可以说不适用于 UNIX 域套接字,因为两个程序不太可能尝试在同一路径上创建一个套接字,所以如果 TIME_WAIT 没有为 UNIX 套接字实现,那么这可能是一个(近似的)解释为什么SO_REUSEADDR 不适用于AF_UNIX

【讨论】:

  • 感谢 Martin 的精彩回复,以及富士通 PDF 的链接。我同意对TIME_WAIT 提及的解释,最后,这不是未指定的,但不适用。只是一件小事:我相信你在最后一个词上犯了一个错误,你应该说“不适用于AF_UNIX”而不是“不适用于AF_INET”。享受美好的一天!
猜你喜欢
  • 2023-03-03
  • 1970-01-01
  • 2013-01-01
  • 1970-01-01
  • 2011-04-21
  • 1970-01-01
  • 2015-12-16
  • 1970-01-01
  • 2012-07-12
相关资源
最近更新 更多