KnigaRead.com/
KnigaRead.com » Компьютеры и Интернет » Программное обеспечение » Уильям Стивенс - UNIX: разработка сетевых приложений

Уильям Стивенс - UNIX: разработка сетевых приложений

На нашем сайте KnigaRead.com Вы можете абсолютно бесплатно читать книгу онлайн Уильям Стивенс, "UNIX: разработка сетевых приложений" бесплатно, без регистрации.
Перейти на страницу:

■ Использовать функцию alarm и сигнал SIGALRM.

■ Задать предел времени в функции select.

■ Использовать более новые параметры сокета SO_RCVTIMEO и SO_SNDTIMEO.

Первый способ легко использовать, но он включает обработку сигналов и, как показано в разделе 20.5, может привести к ситуации гонок. Использование функции select означает, что блокирование происходит в этой функции (с заданным в ней пределом времени) вместо блокирования в вызове функции read, write или connect. Другая альтернатива — использование новых параметров сокета — также проста в использовании, но предоставляется не всеми реализациями.

Функции recvmsg и sendmsg являются наиболее общими из пяти групп предоставляемых функций ввода-вывода. Они объединяют целый ряд возможностей, свойственных других функциям ввода-вывода, позволяя задавать флаг MSG_xxx (как функции recv и send), возвращать или задавать адрес протокола собеседника (как функции recvfrom и sendto), использовать множество буферов (как функции readv и writev). Кроме того, они обеспечивают две новых возможности: возвращение флагов приложению и получение или отправку вспомогательных данных.

В тексте книги мы описываем десять различных форм вспомогательных данных, шесть из которых появились в IPv6. Вспомогательные данные состоят из объектов вспомогательных данных. Перед каждым объектом идет структура cmsghdr, задающая его длину, уровень протокола и тип данных. Пять макросов, начинающихся с префикса CMSG_, используются для создания и анализа вспомогательных данных.

Сокеты могут использоваться со стандартной библиотекой ввода-вывода С, но это добавляет еще один уровень буферизации к уже имеющемуся в TCP. На самом деле недостаток понимания буферизации, выполняемой стандартной библиотекой ввода-вывода, является наиболее общей проблемой при работе с этой библиотекой. Поскольку сокет не является терминальным устройством, общим решением этой потенциальной проблемы будет отключение буферизации стандартного потока ввода-вывода.

Многие производители предоставляют усовершенствованные средства опроса событий без накладных расходов на select и poll. Не стоит увлекаться написанием непереносимого кода, однако иногда преимущества перевешивают риск непереносимости.

Упражнения

1. Что происходит в листинге 14.1, когда мы переустанавливаем обработчик сигналов, если процесс не установил обработчик для сигнала SIGALRM?

2. В листинге 14.1 мы выводим предупреждение, если у процесса уже установлен таймер alarm. Измените эту функцию так, чтобы новое значение alarm для процесса задавалось после выполнения connect до завершения функции.

3. Измените листинг 11.5 следующим образом: перед вызовом функции read вызовите функцию recv с флагом MSG_PEEK. Когда она завершится, вызовите функцию ioctl с командой FIONREAD и выведите число байтов, установленных в очередь в буфере приема сокета. Затем вызовите функцию read для фактического чтения данных.

4. Что происходит с оставшимися в стандартном буфере ввода-вывода данными, если процесс, дойдя до конца функции main, не обнаруживает там функции exit?

5. Примените каждое из двух изменений, описанных после листинга 14.6, и убедитесь в том, что каждое из них решает проблему буферизации.

Глава 15

Доменные протоколы Unix

15.1. Введение

Доменные протоколы Unix — это не набор протоколов, а способ связи клиентов и серверов на отдельном узле, использующий тот же API, который используется для клиентов и серверов на различных узлах, — сокеты или XTI. Доменные протоколы Unix представляют альтернативу методам IPC (Interprocess Communications — взаимодействие процессов), которым посвящен второй том[2] этой серии, применяемым, когда клиент и сервер находятся на одном узле. Подробности действительной реализации доменных сокетов Unix в ядре, происходящем от Беркли, приводятся в третьей части [112].

В домене Unix предоставляются два типа сокетов: потоковые (аналогичные сокетам TCP) и дейтаграммные (аналогичные сокетам UDP). Хотя предоставляется также и символьный сокет, но его семантика никогда не документировалась, он не используется никакой из известных автору программ и не определяется в POSIX.

Доменные сокеты Unix используются по трем причинам.

1. В реализациях, происходящих от Беркли, доменные сокеты Unix часто вдвое быстрее сокетов TCP, когда оба собеседника находятся на одном и том же узле [112, с. 223–224]. Есть приложение, которое использует это преимущество: X Window System. Когда клиент X11 запускается и открывает соединение с сервером X11, клиент проверяет значение переменной окружения DISPLAY, которая задает имя узла сервера, окно и экран. Если сервер находится на том же узле, что и клиент, клиент открывает потоковое соединение с сервером через доменный сокет Unix, в противном случае клиент открывает соединение TCP.

2. Доменные сокеты Unix используются при передаче дескрипторов между процессами на одном и том же узле. Пример мы приводим в разделе 15.7.

3. Более новые реализации доменных сокетов Unix предоставляют регистрационные данные клиента (идентификатор пользователя и идентификаторы группы) серверу, что может служить дополнительной проверкой безопасности. Мы покажем это в разделе 15.8.

Адреса протоколов, используемые для идентификации клиентов и серверов в домене Unix, — это полные имена в обычной файловой системе. Вспомните, что IPv4 использует комбинацию 32-разрядных адресов и 16-разрядных номеров портов для своих адресов протоколов, а IPv6 для своих адресов протоколов использует комбинацию 128-разрядных адресов и 16-разрядных номеров портов. Эти полные имена не являются обычными именами файлов Unix: в общем случае мы не можем читать из этих файлов или записывать в них. Это может делать только программа, связывающая полное имя с доменным сокетом Unix.

15.2. Структура адреса доменного сокета Unix

В листинге 15.1[1] показана структура адреса доменного сокета Unix, задаваемая включением заголовочного файла <sys/un.h>.

Листинг 15.1. Структура адреса доменного сокета Unix: sockaddr_un

struct sockaddr_un {

 uint8_t     sun_len;

 sa_family_t sun_family;    /* AF_LOCAL */

 char        sun_path[104]; /* полное имя, оканчивающееся нулем */

};

ПРИМЕЧАНИЕ

POSIX не задает длину массива sun_path и предупреждает, что разработчику приложения не следует делать предположений об этой длине. Воспользуйтесь оператором sizeof для определения длины массива во время выполнения программы. Убедитесь, что полное имя помещается в этот массив. Длина, скорее всего, будет где-то между 92 и 108. Причина этих ограничений — в артефакте реализации, возникшем еще в 4.2BSD, где требовалось, чтобы структура помещалась в 128-байтовый буфер памяти ядра mbuf.

Полное имя, хранимое в символьном массиве sun_path, должно завершаться нулем. Имеется макрос SUN_LEN, который получает указатель на структуру sockaddr_un и возвращает длину структуры, включая число непустых байтов в полном имени. Неопределенный адрес обозначается пустой строкой, то есть элемент sun_path[0] должен быть равен нулю. Это эквивалент константы INADDR_ANY протокола IPv4 и константы IN6ADDR_ANY_INIT протокола IPv6 для домена Unix.

ПРИМЕЧАНИЕ

В POSIX доменным протоколам Unix дали название «локального IPC», чтобы не подчеркивать зависимость от операционной системы Unix. Историческая константа AF_UNIX становится константой AF_LOCAL. Тем не менее мы будем продолжать использовать термин «домен Unix», так как он стал именем де-факто, независимо от соответствующей операционной системы. Кроме того, несмотря на попытку POSIX исключить зависимость от операционной системы, структура адреса сокета сохраняет суффикс _un!

Пример: функция bind и доменный сокет Unix

Программа, показанная в листинге 15.2, создает доменный сокет Unix, с помощью функции bind связывает с ним полное имя и затем вызывает функцию getsockname и выводит это полное имя.

Листинг 15.2. Связывание полного имени с доменным сокетом Unix

unixdomain/unixbind.c

 1 #include "unp.h"


 2 int

 3 main(int argc, char **argv)

 4 {

 5  int sockfd;

 6  socklen_t len;

 7  struct sockaddr_un addr1, addr2;


 8  if (argc != 2)

 9   err_quit("usage: unixbind <pathname>");


10  sockfd = Socket(AF_LOCAL, SOCK_STREAM, 0);


11  unlink(argv[1]); /* игнорируем возможную ошибку */


12  bzero(&addr1, sizeof(addr1));

13  addr1.sun_family = AF_LOCAL;

Перейти на страницу:
Прокомментировать
Подтвердите что вы не робот:*