Книга: 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.
- Запуск InterBase-сервера
- Расширенная установка InterBase-сервера
- От исключительной полезности к стратегическому ценообразованию
- Firebird РУКОВОДСТВО РАЗРАБОТЧИКА БАЗ ДАННЫХ
- Решетка «упразднить – снизить – повысить – создать»
- Миграция между различными версиями InterBase
- 4.7. Игровые разминки и упражнения-энергизаторы
- Совместимость клиентов и серверов различных версий
- Выбор стратегии ценообразования
- Преобразование XML в реляционную базу данных
- Статистика InterBase-сервера
- 1.1.4. Еще немного терминов