SO_REUSEADDR и AF_UNIX

Факт

В документации POSIX я не вижу ничего, что предотвращало бы использование опции сокета SO_REUSEADDR с AF_UNIX для доменных сокетов UNIX.

Тем не менее, он неизменно терпит неудачу при bind времени, если сокет node уже существует и, кажется, игнорируется и, кажется, требуется разблокировать сокет node в файловой системе, прежде чем вызвать bind; Короче говоря, он не повторно использует адрес. В этой сети есть много потоков, и ни один из них не имеет решения.

Вопрос

Я не буду настаивать, если он не работает, он не работает (кажется, по крайней мере, как на BSD, так и на Linux), и просто возникает вопрос: это нормальное поведение или нет? Есть ли какие-либо указатели, предлагающие его, должны поддерживаться, или наоборот, любые указатели, предлагающие это, не должны? Или это неуказано? Обратите внимание, что вопрос задается в контексте POSIX, а не в каком-либо конкретном контексте платформы.

Я приветствую любую ссылку POSIX по этому вопросу.

Дополнительно: крошечный фрагмент, чтобы не слепо unlink who-know-what

Я видел некоторые потоки в Интернете, предлагая unlink любое node ожидаемого имени до bind. Я чувствую, что это небезопасно, и нужно только отменить node, который уже является сокетом node в этом случае: ex. вероятно, неправильно, чтобы отсоединить текстовый файл с именем mysocket, чтобы воссоздать сокет node с тем же именем. В этой цели здесь крошечный фрагмент:

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

У меня есть только один документ спецификации POSIX, который Системные интерфейсы, поэтому я сделаю все возможное отсюда.

Наша спецификация spelunking приключений должна, конечно, начинаться с 2.10.6 Использование опций, которая определяет опцию SO_REUSEADDR следующим образом:

Параметр SO_REUSEADDR указывает, что правила, используемые при проверке адресов, указанных в bind(), должны разрешать повторное использование локальных адресов. Работа этого параметра зависит от протокола. Значение по умолчанию для SO_REUSEADDR выключено; то есть повторное использование локальных адресов не разрешено.

Этот параграф в принципе отказывается от какой-либо спецификации того, что этот вариант действительно делает, делегируя его спецификации спецификации базового протокола.

Раздел 2.10.17 Использование сокетов для локальных подключений UNIX описывает механизм создания сокетов домена UNIX, но на самом деле единственное, что он говорит нам, это то, что тип сокета постоянный использовать и какую структуру использовать для адресов. Документация для sockattr_un struct сообщает нам только о ее формате, а не о его поведении на bind.

Документация для bind сама, по понятным причинам, является протокольно-агностикой, сообщая нам только, что происходит, когда адрес уже используется, а не обстоятельства, привязка одного и того же сокета.

Хотя он не является стандартным документом POSIX, Fujitsu имеет документ в API сокетов POSIX, который интересен, хотя бы потому, что он не относится конкретно к Linux или BSD. В разделе 2.6.4 этого документа говорится о поведении bind в UNIX-сокетах:

Имя пути, которое должно быть указано в компоненте sun.sun_path, создается как файл в файловой системе, используя bind(). Поэтому процесс, вызывающий bind(), должен иметь права на запись в каталог, в котором должен быть записан файл. Система не удаляет файл. Поэтому он должен быть удален процессом, когда он больше не требуется.

Хотя это ничего не говорит о SO_REUSEADDR в частности, он утверждает, что поведение bind заключается в создании файла и что процесс связывания несет ответственность за его удаление, когда он больше не используется.

Наконец, это описание документа setsockopt имеет следующие значения SO_REUSEADDR:

Указывает, что правила проверки достоверности адресов, указанных для bind(), должны разрешать повторное использование локальных адресов при условии, что это поддерживается протоколом.

Таким образом, хотя это не делает особого упоминания о AF_UNIX, оно подтверждает, что этот параметр не применяется ко всем протоколам.

Я также нашел (неавторизированное) резюме, описывающее намеченную цель SO_REUSEADDR:

Эта опция сокета сообщает ядру, что даже если этот порт занят (в состоянии TIME_WAIT), продолжайте и повторно используйте его. Если он занят, но с другим состоянием, вы все равно получите сообщение об ошибке, которое уже используется.

Состояние TIME_WAIT существует, главным образом, для сокетов, связанных с Интернетом, чтобы избежать привязки новой программы к порту, который недавно использовался другой программой, и непреднамеренно получать пакеты, относящиеся к старой программе. Эта проблема, возможно, неприменима к сокетам UNIX, потому что очень маловероятно, что две программы попытаются создать сокет по тому же пути, поэтому, если TIME_WAIT не реализовано для сокетов UNIX, это может быть одно (приближение) объяснения почему SO_REUSEADDR не относится к AF_UNIX.