• Системный вызов listen() позволяет потоковому сокету принимать входящие соединения от других сокетов.

• Системный вызов accept() принимает соединение от удаленного приложения в «слушающий» потоковый сокет и опционально возвращает адрес удаленного сокета.

• Системный вызов connect() устанавливает соединение с другим сокетом.

В большинстве архитектур Linux (за исключением Alpha и IA-64) все системные вызовы для работы с сокетами реализованы в виде библиотечных функций-оберток вокруг одногоединственного системного вызова, socketcall() (это один из остатков старой реализации сокетов, которая была выполнена в виде отдельного от Linux проекта). Тем не менее в настоящей книге все данные функции называются системными вызовами, поскольку именно так они были изначально реализованы в системе BSD, равно как и во многих других разновидностях UNIX.

Ввод/вывод через сокеты может быть выполнен с помощью традиционных операций read() и write() или же специальных системных вызовов (таких как end(), recv(), sendto() и recvfrom()). По умолчанию все эти вызовы блокируются, если ввод/вывод нельзя выполнить немедленно. Неблокирующие чтение и запись тоже возможны, если включить флаг состояния O_NONBLOCK, используя операции F_SETFL для вызова fcntl() (см. раздел 5.3).

В Linux можно воспользоваться вызовом ioctl(fd, FIONREAD, &cnt), чтобы получить количество непрочитанных байтов, доступных в потоковом сокете, на который ссылается дескриптор fd. В случае с датаграммным сокетом эта операция возвращает количество байтов в следующем непрочитанном сообщении (это значение может быть равным нулю, если следующей датаграммы не существует или она имеет нулевую длину). Эта возможность не предусмотрена стандартом SUSv3.

52.2. Создание сокета: socket()

Системный вызов socket() создает новый сокет.

#include

int socket(int domain, int type, int protocol);

Возвращает файловый дескриптор или -1, если произошла ошибка

Аргументы domain и type обозначают соответственно домен соединения сокета и его тип. Последний обычно принимает одно из двух значений: SOCK_STREAM (для создания потокового сокета) или SOCK_DGRAM (для создания датаграммного).

Для сокетов, описываемых в данной книге, аргумент protocol всегда равен 0. Ненулевые значения применяются для других типов сокетов, которые здесь не затрагиваются. Например, в случае с сырыми сокетами (SOCK_RAW) он равен IPPROTO_RAW.

При успешном выполнении вызов socket() возвращает файловый дескриптор, который будет использоваться для работы с новым сокетом в последующих системных вызовах.

Начиная с версии 2.6.27, ядро Linux позволяет задействовать аргумент type альтернативным способом, предоставляя два нестандартных флага, которые могут применяться к типу сокета с помощью побитового ИЛИ. Значение SOCK_CLOEXEC заставляет ядро включить для нового файлового дескриптора флаг FD_CLOEXEC, используемый по тому же принципу, что и флаг O_CLOEXEC в вызове open() (см. подраздел 4.3.1). Значение SOCK_NONBLOCK заставляет ядро установить для описания файла флаг O_NONBLOCK, делая все последующие операции ввода/вывода с сокетом неблокирующими. Это позволяет избежать дополнительных вызовов fcntl() для достижения того же результата.

52.3. Привязывание сокета к адресу: bind()

Системный вызов bind() привязывает сокет к заданному адресу.

#include

int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen);

Возвращает 0 при успешном завершении или -1 при ошибке

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

Обычно серверный сокет привязывается к общеизвестному адресу; клиентам, подключающимся к серверу, о нем известно заранее.

Серверный сокет можно не привязывать к общеизвестному адресу. Например, в случае с интернет-доменом сервер может пропустить операцию bind() и сразу сделать вызов listen(), что заставит ядро выбрать для соответствующего сокета динамический порт (они будут описаны в подразделе 54.6.1). Позже сервер может использовать функцию getsockname() (см. раздел 57.5) для извлечения адреса из своего сокета. В данном случае сервер должен опубликовать полученный адрес, чтобы клиенты могли найти его сокет. Это можно сделать, зарегистрировав адрес сервера в централизованной службе каталогов, к которой затем подключаются клиенты (например, в системе Sun RPC эта проблема решается с помощью сервера portmapper). Сокет самой службы каталогов, естественно, должен быть доступен по общеизвестному адресу.

52.4. Универсальные структуры для хранения адресов сокетов: struct sockaddr
Перейти на страницу:

Похожие книги