#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. Дополнительное примечание, я рассматриваю это как более надежное поведение, которое должно быть стандартным. То, что применяется к соединению, используемому для прослушивания, часто будет отличаться как от значений по умолчанию, так и от того, что оптимизирует настраиваемый сеанс передачи данных, так что это позволяет приложениям просто устанавливать то, что оптимально для сеанса, не беспокоясь об отмене любых параметров, установленных для прослушивания.