Уильям Стивенс - UNIX: разработка сетевых приложений
- Название:UNIX: разработка сетевых приложений
- Автор:
- Жанр:
- Издательство:Питер
- Год:2007
- Город:Санкт-Петербург
- ISBN:5-94723-991-4
- Рейтинг:
- Избранное:Добавить в избранное
-
Отзывы:
-
Ваша оценка:
Уильям Стивенс - UNIX: разработка сетевых приложений краткое содержание
Новое издание книги, посвященной созданию веб-серверов, клиент-серверных приложений или любого другого сетевого программного обеспечения в операционной системе UNIX, — классическое руководство по сетевым программным интерфейсам, в частности сокетам. Оно основано на трудах Уильяма Стивенса и полностью переработано и обновлено двумя ведущими экспертами по сетевому программированию. В книгу включено описание ключевых современных стандартов, реализаций и методов, она содержит большое количество иллюстрирующих примеров и может использоваться как учебник по программированию в сетях, так и в качестве справочника для опытных программистов.
UNIX: разработка сетевых приложений - читать онлайн бесплатно ознакомительный отрывок
Интервал:
Закладка:
Вторая часть нашей функции приведена в листинге 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 }
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).
POSIX определяет функцию pselect
(повышающую точность таймера с микросекунд до наносекунд) которой передается новый аргумент — указатель на набор сигналов. Это позволяет избежать ситуации гонок (race condition) при перехвате сигналов, о которой мы поговорим более подробно в разделе 20.5.
Функция poll
из System V предоставляет функциональность, аналогичную функции select
. Кроме того, она обеспечивает дополнительную информацию при работе с потоковыми устройствами. POSIX требует наличия и функции select
, и функции poll
, но первая распространена шире.
Упражнения
1. Мы говорили, что набор дескрипторов можно присвоить другому набору дескрипторов, используя оператор присваивания языка С. Как это сделать, если набор дескрипторов является массивом целых чисел? ( Подсказка : посмотрите на свой системный заголовочный файл или .)
2. Описывая в разделе 6.3 условия, при которых функция select
сообщает, что дескриптор готов для записи, мы указали, что сокет должен быть неблокируемым, для того чтобы операция записи возвратила положительное значение. Почему?
3. Что произойдет с программой из листинга 6.1, если мы поставим слово else
перед if
в строке 19?
4. В листинге 6.3 добавьте необходимый код, чтобы позволить серверу использовать максимальное число дескрипторов, допустимое ядром ( Подсказка : изучите функцию setrlimit
.)
5. Посмотрите, что происходит, если в качестве второго аргумента функции shutdown
передается SHUT_RD
. Возьмите за основу код клиента TCP, представленный в листинге 5.3, и выполните следующие изменения: вместо номера порта SERV_PORT
задайте порт 19 (служба chargen
, см. табл. 2.1), а также замените вызов функции str_cli
вызовом функции pause
. Запустите программу, задав IP-адрес локального узла, на котором выполняется сервер chargen
. Просмотрите пакеты с помощью такой программы, как, например, tcpdump
(см. раздел В.5). Что происходит?
6. Почему приложение должно вызывать функцию shutdown
с аргументом SHUT_RDWR
, вместо того чтобы просто вызвать функцию close
?
7. Что происходит в листинге 6.4, когда клиент отправляет RST для завершения соединения?
8. Перепишите код, показанный в листинге 6.5, чтобы вызывать функцию sysconf
для определения максимального числа дескрипторов и размещения соответствующего массива client
в памяти.
Интервал:
Закладка: