SO_REUSEADDR和AF_UNIX

时间:2013-03-30 07:50:02

标签: sockets unix posix

事实

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

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

问题

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

我欢迎任何POSIX参考此事。

额外:一小段不要盲目bind谁知道什么

我在网上看到了一些帖子,建议unlink unlink之前任意节点的bind节点。我觉得它不安全,在这种情况下,应该只取消链接已经是套接字节点的节点:ex。取消链接名为mysocket的文本文件以重新创建具有相同名称的套接字节点可能是错误的。在这个目的中,这是一个小小的片段:

/* 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 … */

1 个答案:

答案 0 :(得分:30)

我只能访问一个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 sockattr_un struct仅告诉我们其格式,而不是bind上的行为。

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

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

  

必须在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域套接字,因为两个程序不太可能尝试在同一路径上创建套接字,所以如果没有为UNIX套接字实现TIME_WAIT那么可能是一个(近似于)解释SO_REUSEADDR不适用于AF_UNIX的原因。