Уильям Стивенс - UNIX: разработка сетевых приложений
- Название:UNIX: разработка сетевых приложений
- Автор:
- Жанр:
- Издательство:Питер
- Год:2007
- Город:Санкт-Петербург
- ISBN:5-94723-991-4
- Рейтинг:
- Избранное:Добавить в избранное
-
Отзывы:
-
Ваша оценка:
Уильям Стивенс - UNIX: разработка сетевых приложений краткое содержание
Новое издание книги, посвященной созданию веб-серверов, клиент-серверных приложений или любого другого сетевого программного обеспечения в операционной системе UNIX, — классическое руководство по сетевым программным интерфейсам, в частности сокетам. Оно основано на трудах Уильяма Стивенса и полностью переработано и обновлено двумя ведущими экспертами по сетевому программированию. В книгу включено описание ключевых современных стандартов, реализаций и методов, она содержит большое количество иллюстрирующих примеров и может использоваться как учебник по программированию в сетях, так и в качестве справочника для опытных программистов.
UNIX: разработка сетевых приложений - читать онлайн бесплатно ознакомительный отрывок
Интервал:
Закладка:
Перевести сокет в режим ввода-вывода, управляемого сигналом, несложно. Сложнее определить условия, которые должны приводить к генерации сигнала SIGIOдля владельца сокета. Это зависит от транспортного протокола.
Сигнал SIGIO и сокеты UDP
Использовать ввод-вывод, управляемый сигналом, с сокетами UDP довольно легко. Сигнал генерируется в следующих случаях:
■ на сокет прибывает дейтаграмма;
■ на сокете возникает асинхронная ошибка.
Таким образом, когда мы перехватываем сигнал SIGIOдля сокета UDP, вызывается функция recvfromкак для чтения дейтаграммы, так и для получения асинхронной ошибки. Асинхронные ошибки, касающиеся UDP-сокетов, обсуждались в разделе 8.9. Напомним, что эти сигналы генерируются, только если сокет UDP является присоединенным (создан с помощью вызова функции connect).
Сигнал SIGIO генерируется для этих двух условий путем вызова макроса sorwakeup, описываемого в книге [128, с. 775, с. 779, с. 784].
Сигнал SIGIO и сокеты TCP
К сожалению, использовать управляемый сигналом ввод-вывод для сокетов TCP почти бесполезно. Проблема состоит в том, что сигнал генерируется слишком часто, а само по себе возникновение сигнала не позволяет выяснить, что произошло. Как отмечается в [128, с. 439], генерацию сигнала SIGIOдля TCP-сокета вызывают все нижеперечисленные ситуации (при условии, что управляемый сигналом ввод-вывод разрешен):
■ на прослушиваемом сокете выполнен запрос на соединение;
■ инициирован запрос на отключение;
■ запрос на отключение выполнен;
■ половина соединения закрыта;
■ данные доставлены на сокет;
■ данные отправлены с сокета (то есть в буфере отправки имеется свободное место);
■ произошла асинхронная ошибка.
Например, если одновременно осуществляются и чтение, и запись в TCP-сокет, то сигнал SIGIOгенерируется, когда поступают новые данные и когда подтверждается прием ранее записанных данных, а обработчик сигнала не имеет возможности различить эти сигналы. Если используется сигнал SIGIO, то для предотвращения блокирования при выполнении функции readили writeTCP-сокет должен находиться в режиме неблокируемого ввода-вывода. Следует использовать сигнал SIGIOлишь с прослушиваемым сокетом TCP, поскольку для прослушиваемого сокета этот сигнал генерируется только при завершении установления нового соединения.
Единственное реальное применение управляемого сигналом ввода-вывода с сокетами, которое удалось обнаружить автору, — это сервер NTP (Network Time Protocol — сетевой протокол синхронизации времени), использующий протокол UDP. Основной цикл этого сервера получает дейтаграмму от клиента и посылает ответ. Но обработка клиентского запроса на этом сервере требует некоторого ненулевого количества времени (больше, чем для нашего тривиального эхо-сервеpa). Серверу важно записать точные отметки времени для каждой принимаемой дейтаграммы, поскольку это значение возвращается клиенту и используется им для вычисления времени обращения к серверу (RTT). На рис. 25.1 показаны два варианта построения такого UDP-сервера.
Рис. 25.1. Два варианта построения UDP-сервера
Большинство UDP-серверов (включая наш эхо-сервер, описанный в главе 8) построены так, как показано на рисунке слева. Однако NTP-сервер использует способ, показанный справа: когда прибывает новая дейтаграмма, она читается обработчиком сигнала SIGIO, который также записывает время прибытия дейтаграммы. Далее дейтаграмма помещается в другую очередь внутри процесса, из которой она будет извлечена, а затем обработана основным циклом сервера. Это усложняет код сервера, но зато обеспечивает точные отметки времени прибытия дейтаграмм.
Вспомните листинг 22.3: процесс может установить параметр сокета IP_RECVDSTADDR, чтобы получить адрес получателя пришедшей UDP-дейтаграммы. Можно возразить, что вместе с полученной дейтаграммой UDP должны быть возвращены два дополнительных фрагмента информации — интерфейс, на котором была получена дейтаграмма (этот интерфейс может отличаться от адреса получателя, если узел использует более типичную модель системы с гибкой привязкой), и время прибытия дейтаграммы.
Для IPv6 интерфейс, на котором была получена дейтаграмма, можно получить, если включен параметр сокета IPV6_PKTINFO (см. раздел 22.8). Аналогичный параметр сокета IP_RECVIF для IPv4 описывался в разделе 22.2.
В FreeBSD также предусмотрен параметр сокета SO_TIMESTAMP, возвращающий время получения дейтаграммы как вспомогательные данные в структуре timeval. В Linux существует флаг SIOCGSTAMP для функции ioctl, которая возвращает структуру timeval, содержащую время прибытия дейтаграммы.
25.3. Эхо-сервер UDP с использованием сигнала SIGIO
В этом разделе мы приведем пример, аналогичный правой части рис. 25.1: UDP-сервер, использующий сигнал SIGIOдля получения приходящих дейтаграмм. Этот пример также иллюстрирует использование надежных сигналов стандарта POSIX.
В данном случае клиент совсем не изменен по сравнению с листингами 8.3 и 8.4, а функция сервера main не изменилась по сравнению с листингом 8.1. Единственные внесенные изменения касаются функции dg_echo, которая будет приведена в следующих четырех листингах. В листинге 25.1 [1] Все исходные коды программ, опубликованные в этой книге, вы можете найти по адресу http://www.piter.com.
представлены глобальные объявления.
Листинг 25.1. Глобальные объявления
//sigio/dgecho01.c
1 #include "unp.h"
2 static int sockfd;
3 #define QSIZE 8 /* размер входной очереди */
4 #define MAXDG 4096 /* максимальный размер дейтаграммы */
5 typedef struct {
6 void *dg_data; /* указатель на текущую дейтаграмму */
7 size_t dg_len; /* длина дейтаграммы */
8 struct sockaddr *dg_sa; /* указатель на sockaddr{} с адресом клиента */
9 socklen_t dg_salen; /* длина sockaddr{} */
10 } DG;
11 static DG dg[QSIZE]; /* очередь дейтаграмм для обработки */
12 static long cntread[QSIZE +1]; /* диагностический счетчик */
13 static int iget; /* следующий элемент для обработки в основном цикле */
14 static int iput; /* следующий элемент для считывания обработчиком
сигналов */
15 static int nqueue; /* количество дейтаграмм в очереди на обработку
в основном цикле */
16 static socklen_t clilen; /* максимальная длина sockaddr{} */
17 static void sig_io(int);
18 static void sig_hup(int);
3-12 Обработчик сигнала SIGIOпомещает приходящие дейтаграммы в очередь. Эта очередь является массивом структур DG, который интерпретируется как кольцевой буфер. Каждая структура содержит указатель на принятую дейтаграмму, ее длину и указатель на структуру адреса сокета, содержащую адрес протокола клиента и размер адреса протокола. В памяти размещается столько этих структур, сколько указано в QSIZE(в данном случае 8), и в листинге 25.2 будет видно, что функция dg_echoдля размещения в памяти всех структур дейтаграмм и адресов сокетов вызывает функцию malloc. Также происходит выделение памяти под диагностический счетчик cntread, который будет рассмотрен чуть ниже. На рис. 25.2 приведен массив структур, при этом предполагается, что первый элемент указывает на 150-байтовую дейтаграмму, а длина связанного с ней адреса сокета равна 16.
Интервал:
Закладка: