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

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) {

32      client[i].fd = connfd; /* сохраняем дескриптор */

33      break;

34     }

35    if (i == OPEN_MAX)

36     err_quit("too many clients");

37    client[i].events = POLLRDNORM;

38    if (i > maxi)

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 для ограничения по времени операции ввода.

Используя эхо-клиент в пакетном режиме с помощью функции select, мы выяснили, что даже если обнаружен признак конца файла, данные все еще могут находиться в канале на пути к серверу или от сервера. Обработка этого сценария требует применения функции shutdown, которая позволяет воспользоваться таким свойством TCP, как возможность половинного закрытия соединения (half-close feature).


Перейти на страницу:
Изменить размер шрифта: