Книга: UNIX: разработка сетевых приложений

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

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

Оглавление книги


Генерация: 1.904. Запросов К БД/Cache: 3 / 1
поделиться
Вверх Вниз