ИМЯ¶
accept - принять
соединение
на сокете
ОБЗОР¶
#include <sys/types.h>
#include <sys/socket.h>
int accept(int s, struct sockaddr *addr,
socklen_t *addrlen);
ОПИСАНИЕ¶
Функция
accept
используется
с сокетами,
ориентированными
на
устанавление
соединения
(
SOCK_STREAM,
SOCK_SEQPACKET и
SOCK_RDM).
Эта функция
извлекает
первый
запрос на
соединение
из очереди
ожидающих
соединений,
создаёт
новый
подключенный
сокет почти
с такими же
параметрами,
что и у
s, и
выделяет для
сокета новый
файловый
дескриптор,
который и
возвращается.
Новый сокет
более не
находится в
слушающем
состоянии.
Исходный
сокет
s не
изменяется
при этом
вызове.
Заметим, что
флаги
файловых
дескрипторов
(те, что можно
установить с
помощью
параметра
F_SETFL
функции
fcntl,
типа
неблокированного
состояния
или
асинхронного
ввода-вывода)
не
наследуются
новым
файловым
дескриптором
после
accept.
Аргумент
s --
это сокет,
который был
создан с
помощью
socket(2),
привязан к
локальному
адресу с
помощью
bind(2), и
слушает
соединения
после
listen(2).
Аргумент
addr --
это
указатель на
структуру
sockaddr. В эту
структуру
помещается
адрес другой
стороны, в
том виде, в
каком он
известен на
коммуникационном
уровне.
Точный
формат
адреса,
передаваемого
в параметре
addr,
определяется
"семейством"
сокета (см.
socket(2)
и страницу
руководства
по
соответствующему
протоколу).
Аргумент
addrlen
является
параметром,
передаваемым
по ссылке:
перед
вызовом он
содержит
размер
структуры,
на которую
ссылается
addr,
а после
вызова --
действительную
длину адреса
в байтах.
Если
addr равен
NULL, он не
заполняется.
Если в
очереди нет
запросов на
соединение,
и на сокет не
установлен
флаг, что он
является
неблокирующим,
accept блокирует
вызвавшую
программу до
появления
соединения.
Если сокет
является
неблокирующим,
а в очереди
нет запросов
на
соединение,
то
accept
возвращает EAGAIN.
Для того,
чтобы
получать
уведомления
о входящих
сооединениях
на сокете,
можно
использовать
select(2) или
poll(2). В
этом случае,
когда придёт
запрос на
новое
соединение,
будет
доставлено
событие
"можно
читать", и
после этого
вы можете
вызвать
accept,
чтобы
получить
сокет для
этого
соединения.
Можно также
настроить
сокет так,
чтобы он
посылал
сигнал
SIGIO,
когда на нём
происходит
какая-либо
активность;
см.
socket(7), где
описаны
детали.
Для
определённых
протоколов,
которые
требуют
явного
подтверждения,
например, DECNet,
системный
вызов
accept
можно
рассматривать
просто как
извлечение
из очереди
следующего
запроса на
соединение,
не
подразумевающее
подтверждение.
Подтверждение,
в свою
очередь,
произойдет
при
следующем
чтении или
записи в
новом
файловом
дескрипторе,
а отказ от
соединения
может
произойти
при закрытии
сокета. В
настоящий
момент под Linux
такую
семантику
имеет только
DECNet.
ЗАМЕЧАНИЯ¶
Не всегда
после
доставки
сигнала
SIGIO
или после
возврата из
select(2) или
poll(2) в
очереди
будет
находиться
соединение.
Оно могло
быть удалено
в той же нити
из-за
асинхронной
сетевой
ошибки или
же в другой
нити до
вызова
accept.
Если такое
произойдет,
то
accept
заблокирует
выполнение
до
следующего
соединения.
Чтобы
убедиться,
что такого
не
произойдет в
любом
случае, на
сокет
s
должен быть
установлен
флаг
O_NONBLOCK (см.
socket(7)).
ВОЗВРАЩАЕМОЕ
ЗНАЧЕНИЕ¶
Этот
системный
вызов
возвращает -1
в случае
ошибки. При
успешном
завершении
возвращается
неотрицательное
целое,
являющееся
дескриптором
сокета.
ОБРАБОТКА
ОШИБОК¶
accept в
реализации Linux
передаёт уже
ожидающие
сетевые
ошибки на
новый сокет,
как код
ошибки из
вызова
accept.
Это
поведение
отличается
от других
реализаций
BSD-сокетов. Для
надёжной
работы
приложения
должны
отслеживать
сетевые
ошибки,
которые
могут
появиться
при работе с
протоколом,
и
обрабатывать
их как
EAGAIN,
повторным
выполнением.
В случае TCP/IP
такими
ошибками
являются
ENETDOWN,
EPROTO,
ENOPROTOOPT,
EHOSTDOWN,
ENONET,
EHOSTUNREACH,
EOPNOTSUPP, и
ENETUNREACH.
ОШИБКИ¶
accept
завершится с
ошибкой
если:
- EAGAIN или EWOULDBLOCK
- сокет
маркирован
как
неблокирующий,
но нет ни
одного
соединения,
которое
можно было
бы принять.
- EBADF
- Дескриптор
неправилен.
- ENOTSOCK
- Дескриптор
ссылается
на файл, а не
на сокет.
- EOPNOTSUPP
- Тип
сокета, на
который
ссылается
дескриптор,
отличается
от SOCK_STREAM.
- EINTR
- Системный
вызов был
прерван
сигналом,
который был
получен
перед тем
как из
очереди
поступило
корректное
соединение.
- ECONNABORTED
- Соединение
было
прервано.
- EINVAL
- Сокет не
слушает
соединения.
- EMFILE
- Было
достигнуто
ограничение
по открытым
файловым
дескриптором
на процесс.
- ENFILE
- Был
достигнут
системный
максимум на
файловые
дескрипторы.
accept может
завершиться
с ошибкой
если:
- Параметр
- EFAULT addr не
находится в
пространстве
адресов с
возможностью
записи.
- ENOBUFS, ENOMEM
- Не
хватает
свободной
памяти. Это
зачастую
означает,
что
выделение
памяти
ограничено
размерами
буфера
сокетов, а не
системной
памятью, но
это не 100%
верно.
- EPROTO
- Ошибка
протокола.
В Linux
accept может
завешиться с
ошибкой
если:
- EPERM
- Правила
межсетевого
экрана
запрещают
соединение.
Вдобавок,
могут также
возвращаться
сетевые
ошибки на
новом сокете
и ошибки,
могущие
возникнуть в
протоколе.
Различные
ядра Linux могут
вернуть
другие
ошибки,
например,
EMFILE,
EINVAL,
ENOSR,
ENOBUFS,
EPERM,
ECONNABORTED,
ESOCKTNOSUPPORT,
EPROTONOSUPPORT,
ETIMEDOUT,
ERESTARTSYS.
СООТВЕТСТВИЕ
СТАНДАРТАМ¶
SVr4, 4.4BSD (функция
accept
впервые
появилась в BSD
4.2). Страница
руководства
BSD
документирует
пять
возможных
кодов ошибок
(EBADF, ENOTSOCK, EOPNOTSUPP, EWOULDBLOCK, EFAULT). SUSv3
документирует
ошибки EAGAIN, EBADF, ECONNABORTED, EINTR,
EINVAL, EMFILE, ENFILE, ENOBUFS, ENOMEM, ENOTSOCK, EOPNOTSUPP, EPROTO,
EWOULDBLOCK. В
дополнение, SUSv2
документирует
EFAULT и ENOSR.
Реализация
accept в Linux _не_
наследует
флаги
сокетов
(типа
O_NONBLOCK). Это
поведение
отличается
от других
реализаций
BSD-сокетов.
Переносимые
программы не
должны
полагаться
на такое
поведение и
всегда
должны
устанавливать
на сокете,
полученном
от
accept, все
требуемые
флаги.
ЗАМЕЧАНИЕ¶
Третий
аргумент
функции
accept
изначально
был объявлен
как int * (это
именно так в
libc4, libc5 и на многих
других
системах,
включая BSD 4.*, SunOS 4
и SGI); черновик
стандарта
POSIX 1003.1g пытался
поменять
этот тип на size_t
*
, и в SunOS 5 это
именно так.
Поздние
черновики POSIX
содержат socklen_t *
,
и в Single Unix Specification и glibc2 это
именно так.
По словам
Линуса
Торвальдса:
В _любой_
разумной
библиотеке
размеры "socklen_t"
и int _должны_
совпадать.
Любой другой
вариант
разламывает
реализацию
BSD-сокетов. В POSIX
сначала
использовали
size_t, но я (и, к
счастью,
кто-то ещё,
хотя и не
слишком
многие)
очень громко
пожаловались.
Такая
реализация
полностью
сломана, как
раз потому,
что size_t очень
редко имеет
тот же
размер, что и
"int", например,
на 64-битных
архитектурах.
Это
необходимо
потому, что
интерфейс
BSD-сокетов
таков, каков
он есть.
В любом
случае, люди
из POSIX наконец
поняли и
создали "socklen_t".
Вообще, с
самого
начала они
просто не
должны были
ничего
трогать, но
по какой-то
причине они
чувствовали,
что должны
использовать
поименованный
тип
(вероятно,
они не
хотели
ударить в
грязь лицом,
сделав
глупость,
поэтому они
тихо
переименовали
место, в
котором
просчитались).
СМОТРИ
ТАКЖЕ¶
bind(2),
connect(2),
listen(2),
select(2),
socket(2)
ПЕРЕВОД¶
Copyright (C) Alexey Mahotkin <alexm@hsys.msk.ru> 1999-2001,
Виктор
Вислобоков
<corochoone@perm.ru> 2003