Длина и формат адреса зависят от адресного семейства. В системном вызове bindуказатель конкретной адресной структуры должен быть приведен к обобщенному адресному типу (struct sockaddr*).
В случае успешного завершения bindвозвращает 0. Если он завершается аварийно, возвращается -1, и переменной errnoприсваивается одно из значений, перечисленных в табл. 15.2.
Таблица 15.2
Значение errno |
Описание |
EBADF |
Неверный файловый дескриптор |
ENOTSOCK |
Файловый дескриптор не ссылается на сокет |
EINVAL |
Файловый дескриптор ссылается на сокет, уже получивший имя |
EADDRNOTAVAIL |
Недопустимый адрес |
EADDINUSE |
У адреса уже есть связанный с ним сокет |
Для сокетов домена AF_UNIXесть несколько дополнительных значений |
EACCESS |
Невозможно создать имя в файловой системе из-за прав доступа |
ENOTDIR, ENAMETOOLONG |
Означает недопустимое имя файла |
Для приема запросов на входящие соединения на базе сокетов серверная программа должна создать очередь для хранения ждущих обработки запросов. Формируется она с помощью системного вызова listen.
#include
int listen(int socket, int backlog);
Система Linux может ограничить количество ждущих обработки соединений, которые могут храниться в очереди. В соответствии с этим максимумом вызов listenзадает длину очереди, равной backlog. Входящие соединения, не превышающие максимальной длины очереди, сохраняются в ожидании сокета; последующим запросам на соединение будет отказано, и клиентская попытка соединения завершится аварийно. Этот механизм реализуется вызовом listenдля того, чтобы можно было сохранить ждущие соединения запросы, пока серверная программа занята обработкой запроса предыдущего клиента. Очень часто параметр backlogравен 5.
Функция listenвернет 0 в случае успешного завершения и -1 в случае ошибки. Как и для системного вызова bind, ошибки могут обозначаться константами EBADF, EINVALИ ENOTSOCK.
Прием запросов на соединение
После создания и именования сокета серверная программа может ждать запросы на выполнение соединения с сокетом с помощью системного вызова accept:
#include
int accept(int socket, struct sockaddr *address, size_t *address_len);
Системный вызов acceptвозвращает управление, когда клиентская программа пытается подключиться к сокету, заданному в параметре socket. Этот клиент — первый из ждущих соединения в очереди данного сокета. Функция acceptсоздает новый сокет для обмена данными с клиентом и возвращает его дескриптор. У нового сокета будет тот же тип, что и у сокета сервера, ждущего запросы на соединения.
Предварительно сокету должно быть присвоено имя с помощью системного вызова bindи у него должна быть очередь запросов на соединение, место для которой выделил системный вызов listen. Адрес вызывающего клиента будет помещен в структуру sockaddr, на которую указывает параметр address. Если адрес клиента не представляет интереса, в этом параметре может задать пустой указатель.
Параметр address_lenзадает длину адресной структуры клиента. Если адрес клиента длиннее, чем это значение, он будет урезан. Перед вызовом acceptв параметре address_lenдолжна быть задана ожидаемая длина адреса. По возвращении из вызова в address_lenбудет установлена реальная длина адресной структуры запрашивающего соединение клиента.
Если нет запросов на соединение, ждущих в очереди сокета, вызов accept будет заблокирован (так что программа не сможет продолжить выполнение) до тех пор, пока клиент не сделает запрос на соединение. Вы можете изменить это поведение, применив флаг O_NONBLOCKв файловом дескрипторе сокета с помощью вызова fcntlв вашей программе следующим образом:
int flags = fcntl(socket, F_GETFL, 0);
fcntl(socket, F_SETFL, O_NONBLOCK | flags);
Функция acceptвозвращает файловый дескриптор нового сокета, если есть запрос клиента, ожидающего соединения, и -1 в случае ошибки. Возможные значения ошибок такие же, как у вызовов bindи listenплюс дополнительная константа EWOULDBLOCKв случае, когда задан флаг O_NONBLOCKи нет ждущих запросов на соединение. Ошибка EINTRвозникнет, если процесс прерван во время блокировки в функции accept.
Читать дальше