accept(2)

НАИМЕНОВАНИЕ
accept, accept4 - принять соединение через сокет

БИБЛИОТЕКА
Standard C library (libc, -lc)

ОБЗОР
#include <sys/socket.h>

int accept(int sockfd, struct sockaddr *_Nullable restrict addr,
           socklen_t *_Nullable restrict addrlen);

#define _GNU_SOURCE             /* Смотрите feature_test_macros(7) */
#include <sys/socket.h>

int accept4(int sockfd, struct sockaddr *_Nullable restrict addr,
           socklen_t *_Nullable restrict addrlen, int flags);

ОПИСАНИЕ
Системный вызов accept() используется с типами сокетов, основанными на соединении (SOCK_STREAM, SOCK_SEQPACKET).Он извлекает первый запрос на соединение из очереди ожидающих соединений для прослушивающего сокета, sockfd, создаёт новый подключенный сокет и возвращает новый файловый дескриптор, указывающий на сокет. Новый сокет более не находится в слушающем состоянии. Исходный сокет sockfd не изменяется при этом вызове.

Аргумент sockfd - сокет, который был создан с помощью socket(2), привязанный к локальному адресу с помощью bind(2) и прослушивающий соединения после listen(2).

Аргумент addr - указатель на структуру sockaddr. Эта структура заполняется адресом равноправного сокета, который известен коммуникационному уровню.Точный формат адреса, возвращаемого в параметре addr, определяется семейством адресов сокета (смотрите socket(2) и страницу руководства соответствующего протокола). Если addr равен NULL, то ничего не помещается; в этом случае addrlen не используется и также должен быть NULL.

С помощью аргумента addrlen осуществляется возврат результата: вызывающая сторона должна указать в нём размер (в байтах) структуры, на которую указывает addr; при возврате он будет содержать фактический размер равноправного адреса.

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

Если в очереди нет ожидающих запросов на соединение и сокет не помечен как неблокирующий, то accept() заблокирует вызвавшую программу до появления соединения. Если сокет помечен как неблокирующий, а в очереди нет запросов на соединение, то accept() завершится с ошибкой EAGAIN или EWOULDBLOCK.

Для того, чтобы получать уведомления о входящих соединениях через сокет, можно использовать select(2), poll(2) или epoll(7). При попытке нового соединения будет выдано сообщение о чтении, после чего вы сможете вызвать accept(), чтобы получить сокет для этого соединения. Можно также настроить сокет так, чтобы он посылал сигнал SIGIO, когда на нём происходит какая-либо активность; более подробно смотрите в socket(7).

Если значение flags равно 0, то вызов accept4() равнозначен accept(). К следующим значениям в flags может быть применена операция поразрядного логического ИЛИ для получения различного поведения:

SOCK_NONBLOCK
    Устанавливает флаг состояния файла O_NONBLOCK для нового открытого файлового описания (смотрите open(2)), на которое ссылается новый файловый дескриптор. Использование данного флага делает ненужными дополнительные вызовы fcntl(2) для достижения того же результата.

SOCK_CLOEXEC
    Устанавливает флаг close-on-exec (FD_CLOEXEC) для нового открытого файлового дескриптора. Смотрите описание флага O_CLOEXEC в open(2) для того, чтобы узнать как это может пригодиться.

ВОЗВРАЩАЕМОЕ ЗНАЧЕНИЕ
При успешном выполнении эти системные вызовы возвращают файловый дескриптор для принятого сокета (неотрицательное целое число). В случае ошибки возвращается -1, errno, которое является сообщением об ошибке, а значение addrlen не изменяется.

  Обработка ошибок
В реализации Linux accept() (и accept4()) передаёт уже ожидающие сетевые ошибки на новый сокет, как код ошибки из вызова accept(). Это поведение отличается от других реализаций BSD-сокетов. Для надёжной работы приложения должны отслеживать сетевые ошибки, которые могут появиться при работе accept() с протоколом и обрабатывать их как EAGAIN повторно выполняя вызов. В случае TCP/IP такими ошибками являются ENETDOWN, EPROTO, ENOPROTOOPT, EHOSTDOWN, ENONET, EHOSTUNREACH, EOPNOTSUPP и ENETUNREACH.

ОШИБКИ

EAGAIN или EWOULDBLOCK
    Сокет помечен как неблокирующий и нет ни одного соединения, которое можно было бы принять. В POSIX.1-2001 и POSIX.1-2008 допускается в этих случаях возвращение ошибки, но не требуется, чтобы эти константы имели одинаковое значение, поэтому переносимое приложение должно проверять обе возможности.

EBADF
    Значение sockfd не является открытым файловым дескриптором.

ECONNABORTED
    Соединение было прервано.

EFAULT
    Аргумент addr не находится в пользовательском пространстве адресов с возможностью записи.

EINTR
    Системный вызов был прерван сигналом, который был перехвачен до того, как было установлено действительное соединение; смотрите signal(7).

EINVAL
    Сокет не слушает соединения или недопустимое значение addrlen (например, отрицательное).

EINVAL
    (accept4()) недопустимое значение в flags.

EMFILE
    Было достигнуто ограничение по количеству открытых файловых дескрипторов на процесс.

ENFILE
    Достигнуто максимальное количество открытых файлов в системе.

ENOBUFS
ENOMEM
    Не хватает свободной памяти. Это зачастую означает, что выделение памяти ограничено размерами буфера сокетов, а не системной памятью.

ENOTSOCK
    Файловый дескриптор sockfd указывает не на каталог.

EOPNOTSUPP
    Указанный сокет не относится к типу SOCK_STREAM.

EPERM
    Правила межсетевого экрана запрещают соединение.

EPROTO
    Ошибка протокола.

Кроме того, могут быть возвращены сетевые ошибки для нового сокета и в соответствии с определениями протокола. Различные ядра Linux могут возвращать другие ошибки, например, ENOSR, ESOCKTNOSUPPORT, EPROTONOSUPPORT, ETIMEDOUT. Значение ошибки ERESTARTSYS можно увидеть при трассировке.

ВЕРСИИ
В Linux новый сокет, возвращаемый accept(), not не наследует флагов состояния файла, таких как O_NONBLOCK и O_ASYNC из прослушивающего сокета. Это поведение отличается от канонической реализации сокетов BSD. Переносимые программы не должны полагаться на наследование флагов состояния файла или их отсутствие, а всегда должны устанавливать на сокете, полученном от accept(), все требуемые флаги.

СТАНДАРТЫ

accept()
    POSIX.1-2008.

accept4()
    Linux.

ИСТОРИЯ

accept()
    POSIX.1-2001, SVr4, 4.4BSD (accept() впервые появился в BSD 4.2 ).

accept4()
    Linux 2.6.28, glibc 2.10.

ПРИМЕЧАНИЯ
После доставки SIGIO не всегда может быть установлено ожидающее соединение или select(2), poll(2) или epoll(7) вернут событие доступности чтения, так как подключение может быть удалено из-за асинхронной сетевой ошибкой или другой поток был вызван раньше accept(). Если это случилось, то вызов блокируется, ожидая прибытия следующего подключения. Чтобы гарантировать, что функция accept() никогда не блокируется, для переданного сокета sockfd должен быть установлен флаг O_NONBLOCK (см. socket(7)).

Для определённых протоколов, которые требуют явного подтверждения, например, DECnet, accept() можно рассматривать просто как извлечение из очереди следующего запроса на соединение, не подразумевающее подтверждения. Подтверждение, в свою очередь, произойдет при следующем чтении или записи в новом файловом дескрипторе, а отказ от соединения может произойти при закрытии нового сокета. В настоящее время, под Linux такую семантику имеет только DECnet.

  Тип socklen_t
В первоначальной реализации сокетов в BSD (и в других системах) третий аргумент accept() объявлялся как int *. В стандарте черновика POSIX.1g захотели изменить его на size_t *. В поздних версиях стандарта POSIX и в glibc 2.x используется socklen_t *.

ПРИМЕРЫ
См. bind(2).

СМОТРИТЕ ТАКЖЕ
bind(2), connect(2), listen(2), select(2), socket(2), socket(7)

ПЕРЕВОД
Русский перевод этой страницы руководства разработал(и) Dmitry Bolkhovskikh <d20052005@yandex.ru>, Yuri Kozlov <yuray@komyakino.ru>, Aleksandr Felda <isk8da@gmail.com> и Kirill Rekhov <krekhov.dev@gmail.com>

Этот перевод является свободной программной документацией; он распространяется на условиях общедоступной лицензии GNU (GNU General Public License - GPL, https://www.gnu.org/licenses/gpl-3.0.html версии 3 или более поздней) в отношении авторского права, но БЕЗ КАКИХ-ЛИБО ГАРАНТИЙ.

Если вы обнаружите какие-либо ошибки в переводе этой страницы руководства, пожалуйста, сообщите об этом разработчику(ам) по его(их) адресу(ам) электронной почты или по адресу списка рассылки русских переводчиков (<debian-l10n-russian@lists.debian.org>).