Наследуются ли параметры сокета через accept () от прослушивающего сокета?

#c #sockets #posix

#c #сокеты #posix

Вопрос:

Предположим, что для прослушивающего сокета, переданного в accept , установлены параметры не по умолчанию с помощью setsockopt . Наследуются ли эти параметры (некоторые или все из них?) результирующими файловыми дескрипторами для принятых подключений?

Комментарии:

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

2. «Тест» — хороший ответ, если вы пишете программу для конкретной цели, которая полностью находится под вашим контролем, например, для встроенной системы, где вы выбираете оборудование, на котором она работает, версию ядра / библиотеки / программного обеспечения и т.д. Однако это не очень полезно, если ваша цель — написание переносимых приложений. И даже во встроенных системах использование «тестового» метода ответов на подобные вопросы сопряжено с большими затратами: это означает, что когда вы обнаружите, что вам нужно обновить программное обеспечение, вам придется беспокоиться о том, верны ли полученные ранее результаты, и если нет, вы можете застрять со старым программным обеспечением…

3. @Gil: Вот почему вы пишете с четко определенным поведением, а не случайными вещами, которые вы обнаружили, тестируя текущую реализацию.

4. @Существует несколько параметров, которые нельзя протестировать как таковые, например, параметр сокета SO_BINDTODEVICE доступен только для setsockopt, а не для getsockopt. Следовательно, можно только попробовать установить этот параметр и надеяться, что ваш сокет привязан к этому интерфейсу. Вы получите подтверждение только после того, как действительно получите пакет, и углубитесь в rcvmsg с помощью IP_PACKETINFO, чтобы определить, какой интерфейс получил пакет. Я уже выполнял это упражнение в реальном продукте.

5. этот небольшой фрагмент «Этот параметр, как и многие другие, будет унаследован сокетом, возвращаемым accept (2), если он был установлен на прослушивающем сокете». в разделе TCP_USER_TIMEOUT из man7.org/linux/man-pages/man7/tcp.7.html казалось бы, указывает на то, что некоторые есть, а некоторые нет

Ответ №1:

Некоторые параметры сокета обрабатываются на более низких уровнях системы. Хотя большинство параметров сокета можно установить с помощью setsockopt. Ссылка: man setsockopt И поскольку вы упоминаете только POSIX в любом Linux, в общем, в качестве своей области. accept() (Ссылка: man accept ) имеет определенную свободу действий в отношении того, какие параметры сокета следует унаследовать, а какие параметры отклонить от прослушивающего fd.

accept () не изменяет исходный сокет, переданный ему в качестве аргумента. Новый сокет, возвращаемый accept(), не наследует от прослушивающего сокета флаги состояния файла, такие как O_NONBLOCK, O_ASYNC.

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

страницы руководства и коды реализации на вашем компьютере были бы наиболее подходящей спецификацией для поведения accept().В нескольких вариантах Linux не существует общей или стандартной спецификации.

Комментарии:

1. O_ параметры отличаются от setsockopt options.

2. Хотя они не могут быть установлены с помощью setsockopt(), флаги состояния файла или O_ параметры могут быть применены к fd сокета с помощью системного вызова fcntl(). Затем сокет может быть передан accept().Ссылка: man socket

3. Я хочу сказать, что вопрос касается параметров сокета, а не флагов описания открытого файла.

4. Поскольку флаги состояния файла также могут содержать fd сокета (хотя и не имеют отношения к параметрам сокета) Я просто добавляю здесь точку w.r.to поведение accept().

5. То, что вы сказали, НЕВЕРНО. O_NONBLOCK может быть унаследован.

Ответ №2:

Нет, они не обязательно наследуются. Попробуйте этот пример, который устанавливает размер буфера приема ( SO_RCVBUF ) на начальном сокете в значение, отличное от значения по умолчанию, а затем сравнивает результат с унаследованным сокетом. Запустите этот код, который прослушивает TCP-порт 12345, а затем подключитесь к нему из любой другой программы.

 #include <errno.h>
#include <netinet/in.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>

void die(const char *f)
{
  printf("%s: %sn", f, strerror(errno));
  exit(1);
}

int main(void)
{
  int s = socket(AF_INET, SOCK_STREAM, 0);
  if(s < 0)
    die("socket");

  int rcvbuf;
  socklen_t optlen = sizeof(rcvbuf);
  if(getsockopt(s, SOL_SOCKET, SO_RCVBUF, amp;rcvbuf, amp;optlen) < 0)
    die("getsockopt (1)");
  printf("initial rcvbuf: %dn", rcvbuf);
  rcvbuf *= 2;
  if(setsockopt(s, SOL_SOCKET, SO_RCVBUF, amp;rcvbuf, sizeof(rcvbuf)) < 0)
    die("setsockopt");
  printf("set rcvbuf to %dn", rcvbuf);

  struct sockaddr_in sin;
  memset(amp;sin, 0, sizeof(sin));
  sin.sin_family = AF_INET;
  sin.sin_port = htons(12345);
  sin.sin_addr.s_addr = INADDR_ANY;
  if(bind(s, (struct sockaddr *)amp;sin, sizeof(sin)) < 0)
    die("bind");

  if(listen(s, 10) < 0)
    die("listen");

  struct sockaddr_in client_addr;
  socklen_t addr_len = sizeof(client_addr);
  int s2 = accept(s, (struct sockaddr *)amp;client_addr, amp;addr_len);
  if(s2 < 0)
    die("accept");
  printf("accepted connectionn");
  optlen = sizeof(rcvbuf);
  if(getsockopt(s2, SOL_SOCKET, SO_RCVBUF, amp;rcvbuf, amp;optlen) < 0)
    die("getsockopt (2)");

  printf("new rcvbuf: %dn", rcvbuf);

  return 0;
}
  

Результат на компьютере под управлением Linux 3.0.0-21-общий:

 initial rcvbuf: 87380
set rcvbuf to 174760
accepted connection
new rcvbuf: 262142
  

Комментарии:

1. Я не думаю, что это так (но я могу ошибаться, вот ссылка). «SO_RCVBUF устанавливает или получает максимальный буфер приема сокета в байтах. Ядро удваивает это значение (чтобы освободить место для накладных расходов на ведение бухгалтерского учета), когда оно задается с помощью setsockopt(2), и это удвоенное значение возвращается getsockopt(2). Значение по умолчанию задается файлом /proc/sys/net/core/rmem_default, а максимально допустимое значение задается файлом /proc/sys/net/core/rmem_max. Минимальное (удвоенное) значение для этого параметра равно 256». linux.die.net/man/7/socket .

2. @fayyazkl: О, интересно, я думаю, это объясняет, почему новый rcvbuf отличается как от исходного значения по умолчанию, так и от измененного значения. Я изменил это, чтобы сделать другое, getsockopt сразу после setsockopt , в исходном сокете, и это неожиданно вернуло другое значение, которое затем было унаследовано в новом сокете. Однако только потому, что в данном случае это произошло наследованием, это не означает, что все параметры обязательно наследуются или что на такое поведение можно положиться.

3. Конечно, согласен. Я указал, например, для SO_BINDTODEVICE (в комментариях к вопросу), для которого разрешен только set. Таким образом, вы даже не можете протестировать с помощью getsock, чтобы узнать, было ли оно установлено, или, если на то пошло, привязка остается с тем же интерфейсом после принятия. Я уверен, что даже если многие параметры наследуются безопасно, есть другие с другим поведением

Ответ №3:

Параметры сокета — это место, куда попадают вещи, которые не подходят другим. Итак, ожидается, что разные параметры сокета будут иметь разное поведение при наследовании. Наследовать или нет параметр сокета, решается в каждом конкретном случае.

Ответ №4:

Ответ отрицательный для реализаций, соответствующих POSIX, как я прочитал.

Из спецификации POSIX-2017 для accept():

Функция accept () должна извлечь первое соединение в очереди ожидающих подключений, создать новый сокет с тем же протоколом типа сокета и семейством адресов, что и указанный сокет, и выделить новый файловый дескриптор для этого сокета.

Обратите внимание, что это явно «новый сокет», а не «полная или частичная копия сокета, не находящегося в очереди», поэтому параметры не должны отличаться от параметров по умолчанию для этого типа сокета и семейства адресов. Хотя поведение копирования может быть желательным, это оставлено в качестве интерфейса расширения, который может иметь платформа. Однако я не видел, чтобы какая-либо платформа реализовывала его, поэтому его можно было бы добавить в стандарт. Следовательно, приложение должно использовать getsockopt() / setsockopt() для копирования любых атрибутов, отличающихся от значений по умолчанию, из сокета очереди в возвращаемый сокет, а не ответственность интерфейса, перед любым использованием этого сокета для отправки или получения данных.

Комментарии:

1. Дополнительное примечание, я рассматриваю это как более надежное поведение, которое должно быть стандартным. То, что применяется к соединению, используемому для прослушивания, часто будет отличаться как от значений по умолчанию, так и от того, что оптимизирует настраиваемый сеанс передачи данных, так что это позволяет приложениям просто устанавливать то, что оптимально для сеанса, не беспокоясь об отмене любых параметров, установленных для прослушивания.