39 maxi = i; /* максимальный индекс в массиве client[] */
40 if (--nready <= 0)
41 continue; /* больше нет дескрипторов, готовых для чтения */
42 }
43 for (i = 1; i <= maxi; i++) { /* проверяем все клиенты на наличие
данных */
44 if ((sockfd = client[i].fd) < 0)
45 continue;
46 if (client[i].revents & (POLLRDNORM | POLLERR)) {
47 if ((n = Read(sockfd, buf, MAXLINE)) < 0) {
48 if (errno == ECONNRESET) {
49 /* соединение переустановлено клиентом */
50 Close(sockfd);
51 client[i].fd = -1;
52 } else
53 err_sys("readline error");
54 } else if (n == 0) {
55 /* соединение закрыто клиентом */
56 Close(sockfd);
57 client[i].fd = -1;
58 } else
59 Writen(sockfd, line, n);
60 if (--nready <= 0)
61 break; /* больше нет дескрипторов, готовых для чтения */
62 }
63 }
64 }
65 }
Вызов
функции poll, проверка нового соединения
26-42
Мы вызываем функцию
poll
для ожидания нового соединения либо данных на существующем соединении. Когда новое соединение принято, мы находим первый свободный элемент в массиве
client
— это первый элемент с отрицательным дескриптором. Обратите внимание, что мы начинаем поиск с индекса 1, поскольку элемент
client[0]
используется для прослушиваемого сокета. Когда свободный элемент найден, мы сохраняем дескриптор и устанавливаем событие
POLLRDNORM
.
Проверка данных на существующем соединении
43-63
Два события, которые нас интересуют, — это
POLLRDNORM
и
POLLERR
. Второй флаг в элементе
event
мы не устанавливали, поскольку этот флаг возвращается всегда, если соответствующее условие выполнено. Причина, по которой мы проверяем событие
POLLERR
, в том, что некоторые реализации возвращают это событие, когда приходит сегмент RST, другие же в такой ситуации возвращают событие
POLLRDNORM
. В любом случае мы вызываем функцию
read
, и если произошла ошибка, эта функция возвратит ее. Когда существующее соединение завершается клиентом, мы просто присваиваем элементу
fd
значение -1.
6.12. Резюме
В Unix существует пять различных моделей ввода-вывода:
блокируемый ввод-вывод;
неблокируемый ввод-вывод;
мультиплексирование ввода-вывода;
управляемый сигналом ввод-вывод;
асинхронный ввод-вывод.
По умолчанию используется блокируемый ввод-вывод, и этот вариант встречается наиболее часто. Неблокируемый ввод-вывод и управляемый сигналом ввод-вывод мы рассмотрим в последующих главах. В этой главе мы рассмотрели мультиплексирование ввода-вывода. Асинхронный ввод-вывод определяется в стандарте POSIX, но поддерживающих его реализаций не так много.
Наиболее часто используемой функцией для мультиплексирования ввода- вывода является функция
select
. Мы сообщаем этой функции, какие дескрипторы нас интересуют (для чтения, записи или условия ошибки), а также передаем ей максимальное время ожидания и максимальное число дескрипторов (увеличенное на единицу). Большинство вызовов функции
select
определяют количество дескрипторов, готовых для чтения, и, как мы отметили, единственное условие исключения при работе с сокетами — это прибытие внеполосных данных (см. главу 21). Поскольку функция
select
позволяет ограничить время блокирования функции, мы используем это свойство в листинге 14.3 для ограничения по времени операции ввода.