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

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

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

Мы разделили эту таблицу на три части: первые четыре константы относятся ко вводу, следующие три — к выводу, а последние три — к ошибкам. Обратите внимание, что последние три константы не могут устанавливаться в элементе events, но всегда возвращаются в revents, когда выполняется соответствующее условие.

Существует три класса данных, различаемых функцией poll: обычные, приоритетные и данные с высоким приоритетом. Эти термины берут начало в реализациях, основанных на потоках (см. рис. 31.5).

ПРИМЕЧАНИЕ

Константа POLLIN может быть задана путем логического сложения констант POLLRDNORM и POLLRDBAND. Константа POLLIN существовала еще в реализациях SVR3, которые предшествовали полосам приоритета в SVR4, то есть эта константа существует в целях обратной совместимости. Аналогично, константа POLLOUT эквивалентна POLLWRNORM, и первая из них предшествовала второй.

Для сокетов TCP и UDP при описанных условиях функция poll возвращает указанный флаг revent. К сожалению, в определении функции poll стандарта POSIX имеется множество слабых мест (неоднозначностей):

■ Все регулярные данные TCP и все данные UDP считаются обычными.

■ Внеполосные данные TCP (см. главу 24) считаются приоритетными.

■ Когда считывающая половина соединения TCP закрывается (например, если получен сегмент FIN), это также считается равнозначным обычным данным, и последующая операция чтения возвратит нуль.

■ Наличие ошибки для соединения TCP может расцениваться либо как обычные данные, либо как ошибка (POLLERR). В любом случае последующая функция read возвращает -1, что сопровождается установкой переменной errno в соответствующее значение. Это происходит при получении RST или истечении таймера.

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

Число элементов в массиве структур задается аргументом nfds.

ПРИМЕЧАНИЕ

Исторически этот аргумент имел тип long без знака, что является некоторым излишеством. Достаточно будет типа int без знака. В Unix 98 для этого аргумента определяется новый тип — nfds_t.

Аргумент timeout определяет, как долго функция находится в ожидании перед завершением. Положительным значением задается количество миллисекунд — время ожидания. В табл. 6.3 показаны возможные значения аргумента timeout.


Таблица 6.3. Значения аргумента timeout для функции poll

Значение аргумента timeout Описание INFTIM Ждать вечно 0 Возвращать управление немедленно, без блокирования >0 Ждать в течение указанного числа миллисекунд

Константа INFTIM определена как отрицательное значение. Если таймер в данной системе не обеспечивает точность порядка миллисекунд, значение округляется в большую сторону до ближайшего поддерживаемого значения.

ПРИМЕЧАНИЕ

POSIX требует, чтобы константа INFTIM была определена в заголовочном файле <poll.h>, но многие системы все еще определяют ее в заголовочном файле <sys/stropts.h>.

Как и в случае функции select, любой тайм-аут, установленный для функции poll, ограничивается снизу разрешающей способностью часов в конкретной реализации (обычно 10 мс).

Функция poll возвращает -1, если произошла ошибка, 0 — если нет готовых дескрипторов до истечения времени таймера, иначе возвращается число дескрипторов с ненулевым элементом revents.

Если нас больше не интересует конкретный дескриптор, достаточно установить элемент fd структуры pollfd равным отрицательному значению. В этом случае элемент events будет проигнорирован, а элемент revents при возвращении функции будет сброшен в нуль.

Вспомните наши рассуждения в конце раздела 6.3 относительно константы FD_SETSIZE и максимального числа дескрипторов в наборе в сравнении с максимальным числом дескрипторов для процесса. У нас не возникает подобных проблем с функцией poll, поскольку вызывающий процесс отвечает за размещение массива структур pollfd в памяти и за последующее сообщение ядру числа элементов в массиве. Не существует типа данных фиксированного размера, аналогичного fd_set, о котором знает ядро.

ПРИМЕЧАНИЕ

POSIX требует наличия и функции select, и функции poll. Но если сравнивать их с точки зрения переносимости, то функцию select в настоящее время поддерживает больше систем, чем функцию poll. POSIX определяет также функцию pselect — усовершенствованную версию функции select, которая обеспечивает возможность блокирования сигналов и предоставляет лучшую разрешающую способность по времени, а для функции poll ничего подобного в POSIX нет.

6.11. Эхо-сервер TCP (еще раз)

Теперь мы изменим наш эхо-сервер TCP из раздела 6.8, используя вместо функции select функцию poll. В предыдущей версии сервера, работая с функцией select, мы должны были выделять массив client вместе с набором дескрипторов rset (см. рис. 6.12). С помощью функции poll мы разместим в памяти массив структур pollfd. В нем же мы будем хранить и информацию о клиенте, не создавая для нее другой массив. Элемент fd этого массива мы обрабатываем тем же способом, которым обрабатывали массив client (см. рис. 6.12): значение -1 говорит о том, что элемент не используется, а любое другое значение является номером дескриптора. Вспомните из предыдущего раздела, что любой элемент в массиве структур pollfd, передаваемый функции poll с отрицательным значением элемента fd, просто игнорируется.

В листинге 6.5 показана первая часть кода нашего сервера.

Листинг 6.5. Первая часть сервера TCP, использующего функцию poll

//tcpcliserv/tcpservpoll01.с

 1 #include "unp.h"

 2 #include <1imits.h> /* для OPEN_MAX */


 3 int

 4 main(int argc, char **argv)

 5 {

 6  int i, maxi, listenfd, connfd, sockfd;

 7  int nready;

 8  ssize_t n;

 9  char buf[MAXLINE];

10  socklen_t clilen;

11  struct pollfd client[OPEN_MAX];

12  struct sockaddr_in cliaddr, servaddr;


13  listenfd = Socket(AF_INET, SOCK_STREAM, 0);


14  bzero(&servaddr, sizeof(servaddr));

15  servaddr.sin_family = AF_INET;

16  servaddr.sin_addr.s_addr = htonl(INADDR_ANY);

17  servaddr.sin_port = htons(SERV_PORT);


18  Bind(listenfd, (SA*)&servaddr, sizeof(servaddr));


19  Listen(listenfd, LISTENQ);


20  client[0].fd = listenfd;

21  client[0].events = POLLRDNORM;

22  for (i = 1; i < OPEN_MAX; i++)

23   client[i].fd = -1; /* -1 означает, что элемент свободен */

24  maxi = 0; /* максимальный индекс массива client[] */

Размещение массива структур pollfd в памяти

11 Мы объявляем массив структур pollfd размером OPEN_MAX. Не существует простого способа определить максимальное число дескрипторов, которые могут быть открыты процессом. Мы снова столкнемся с этой проблемой в листинге 13.1. Один из способов ее решения — вызвать функцию POSIX sysconf с аргументом _SC_OPEN_MAX [110, с. 42-44], а затем динамически выделять в памяти место для массива соответствующего размера. Однако функция sysconf может возвратить некое «неопределенное» значение, и в этом случае нам придется задавать ограничение самим. Здесь мы используем только константу OPEN_MAX стандарта POSIX.

Инициализация

20-24 Мы используем первый элемент в массиве client для прослушиваемого сокета и присваиваем дескрипторам для оставшихся элементов -1. Мы также задаем в качестве аргумента функции poll событие POLLRDNORM, чтобы получить уведомление от этой функции в том случае, когда новое соединение будет готово к приему. Переменная maxi содержит максимальный индекс массива client, используемый в настоящий момент.

Вторая часть нашей функции приведена в листинге 6.6.

Листинг 6.6. Вторая часть сервера TCP, использующего функцию poll

//tcpcliserv/tcpservpoll01.c

25  for (;;) {

26   nready = Poll(client, maxi + 1, INFTIM);


27   if (client[0].revents & POLLRDNORM) { /* новое соединение

                                              с клиентом */

28    clilen = sizeof(cliaddr);

29    connfd = Accept(listenfd. (SA*)&cliaddr, &clilen);


30    for (i = 1; i < OPEN_MAX; i++)

31     if (client[1].fd < 0) {

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