struct sockaddr_un {
sa_family_t sun_family; /* AF_UNIX */
char sun_path[]; /* Путь к файлу */
};
Для того чтобы адреса разных типов могли передаваться в системные вызовы для обработки сокетов, все адресные форматы описываются похожей структурой, которая начинается с поля (в данном случае sun_family), задающего тип адреса (домен сокета). В домене AF_UNIX адрес задается именем файла в поле структуры sun_path.
В современных системах Linux тип sa_family_t, описанный в стандарте X/Open как объявляемый в заголовочном файле sys/un.h, интерпретируется как тип short. Кроме того, размер pathname, задаваемого в поле sun_path, ограничен (в Linux указывается 108 символов; в других системах может применяться именованная константа, например, UNIX_MAX_PATH). Поскольку размер адресной структуры может меняться, многие системные вызовы сокетов требуют или предоставляют на выходе длину, которая будет использоваться для копирования конкретной адресной структуры.
В домене AF_INET адрес задается с помощью структуры с именем sockaddr_in, определенной в файле netinet/in.h, которая содержит как минимум следующие элементы:
struct sockaddr_in {
short int sin_family; /* AF_INET */
unsigned short int sin_port; /* Номер порта */
struct in_addr sin_addr; /* Интернет-адрес */
};
Структура IP-адреса типа in_addr определена следующим образом:
struct in_addr {
unsigned long int s_addr;
};
Четыре байта IP-адреса образуют одно 32-разрядное значение. Сокет домена AF_INET полностью описывается IP-адресом и номером порта. С точки зрения приложения все сокеты действуют как файловые дескрипторы, и их адреса задаются уникальными целочисленными значениями.
Именование сокета
Для того чтобы сделать сокет (созданный с помощью вызова socket) доступным для других процессов, серверная программа должна присвоить сокету имя. Сокеты домена AF_UNIX связаны с полным именем файла в файловой системе, как вы видели в программе-примере server1. Сокеты домена AF_INET связаны с номером IP-порта.
#include <sys/socket.h>
int bind(int socket, const struct sockaddr *address, size_t address len);
Системный вызов bind присваивает адрес, заданный в параметре address, неименованному сокету, связанному с дескриптором сокета socket. Длина адресной структуры передается в параметре address_len:
Длина и формат адреса зависят от адресного семейства. В системном вызове bind указатель конкретной адресной структуры должен быть приведен к обобщенному адресному типу (struct sockaddr*).
В случае успешного завершения bind возвращает 0. Если он завершается аварийно, возвращается -1, и переменной errno присваивается одно из значений, перечисленных в табл. 15.2.
Таблица 15.2
Значение errno |
Описание |
|---|---|
EBADF
|
Неверный файловый дескриптор |
ENOTSOCK
|
Файловый дескриптор не ссылается на сокет |
EINVAL
|
Файловый дескриптор ссылается на сокет, уже получивший имя |
EADDRNOTAVAIL
|
Недопустимый адрес |
EADDINUSE
|
У адреса уже есть связанный с ним сокет |
Для сокетов домена AF_UNIX есть несколько дополнительных значений |
|
EACCESS
|
Невозможно создать имя в файловой системе из-за прав доступа |
ENOTDIR, ENAMETOOLONG |
Означает недопустимое имя файла |
Создание очереди сокетов
Для приема запросов на входящие соединения на базе сокетов серверная программа должна создать очередь для хранения ждущих обработки запросов. Формируется она с помощью системного вызова listen.
#include <sys/socket.h>
int listen(int socket, int backlog);
Система Linux может ограничить количество ждущих обработки соединений, которые могут храниться в очереди. В соответствии с этим максимумом вызов listen задает длину очереди, равной backlog. Входящие соединения, не превышающие максимальной длины очереди, сохраняются в ожидании сокета; последующим запросам на соединение будет отказано, и клиентская попытка соединения завершится аварийно. Этот механизм реализуется вызовом listen для того, чтобы можно было сохранить ждущие соединения запросы, пока серверная программа занята обработкой запроса предыдущего клиента. Очень часто параметр backlog равен 5.
Функция listen вернет 0 в случае успешного завершения и -1 в случае ошибки. Как и для системного вызова bind, ошибки могут обозначаться константами EBADF, EINVAL И ENOTSOCK.
Прием запросов на соединение
После создания и именования сокета серверная программа может ждать запросы на выполнение соединения с сокетом с помощью системного вызова accept: