- Любовные романы
- Фантастика и фэнтези
- Ненаучная фантастика
- Ироническое фэнтези
- Научная Фантастика
- Фэнтези
- Ужасы и Мистика
- Боевая фантастика
- Альтернативная история
- Космическая фантастика
- Попаданцы
- Юмористическая фантастика
- Героическая фантастика
- Детективная фантастика
- Социально-психологическая
- Боевое фэнтези
- Русское фэнтези
- Киберпанк
- Романтическая фантастика
- Городская фантастика
- Технофэнтези
- Мистика
- Разная фантастика
- Иностранное фэнтези
- Историческое фэнтези
- LitRPG
- Эпическая фантастика
- Зарубежная фантастика
- Городское фентези
- Космоопера
- Разное фэнтези
- Книги магов
- Любовное фэнтези
- Постапокалипсис
- Бизнес
- Историческая фантастика
- Социально-философская фантастика
- Сказочная фантастика
- Стимпанк
- Романтическое фэнтези
- Ироническая фантастика
- Детективы и Триллеры
- Проза
- Юмор
- Феерия
- Новелла
- Русская классическая проза
- Современная проза
- Повести
- Контркультура
- Русская современная проза
- Историческая проза
- Проза
- Классическая проза
- Советская классическая проза
- О войне
- Зарубежная современная проза
- Рассказы
- Зарубежная классика
- Очерки
- Антисоветская литература
- Магический реализм
- Разное
- Сентиментальная проза
- Афоризмы
- Эссе
- Эпистолярная проза
- Семейный роман/Семейная сага
- Поэзия, Драматургия
- Приключения
- Детская литература
- Загадки
- Книга-игра
- Детская проза
- Детские приключения
- Сказка
- Прочая детская литература
- Детская фантастика
- Детские стихи
- Детская образовательная литература
- Детские остросюжетные
- Учебная литература
- Зарубежные детские книги
- Детский фольклор
- Буквари
- Книги для подростков
- Школьные учебники
- Внеклассное чтение
- Книги для дошкольников
- Детская познавательная и развивающая литература
- Детские детективы
- Домоводство, Дом и семья
- Юмор
- Документальные книги
- Бизнес
- Работа с клиентами
- Тайм-менеджмент
- Кадровый менеджмент
- Экономика
- Менеджмент и кадры
- Управление, подбор персонала
- О бизнесе популярно
- Интернет-бизнес
- Личные финансы
- Делопроизводство, офис
- Маркетинг, PR, реклама
- Поиск работы
- Бизнес
- Банковское дело
- Малый бизнес
- Ценные бумаги и инвестиции
- Краткое содержание
- Бухучет и аудит
- Ораторское искусство / риторика
- Корпоративная культура, бизнес
- Финансы
- Государственное и муниципальное управление
- Менеджмент
- Зарубежная деловая литература
- Продажи
- Переговоры
- Личная эффективность
- Торговля
- Научные и научно-популярные книги
- Биофизика
- География
- Экология
- Биохимия
- Рефераты
- Культурология
- Техническая литература
- История
- Психология
- Медицина
- Прочая научная литература
- Юриспруденция
- Биология
- Политика
- Литературоведение
- Религиоведение
- Научпоп
- Психология, личное
- Математика
- Психотерапия
- Социология
- Воспитание детей, педагогика
- Языкознание
- Беременность, ожидание детей
- Транспорт, военная техника
- Детская психология
- Науки: разное
- Педагогика
- Зарубежная психология
- Иностранные языки
- Филология
- Радиотехника
- Деловая литература
- Физика
- Альтернативная медицина
- Химия
- Государство и право
- Обществознание
- Образовательная литература
- Учебники
- Зоология
- Архитектура
- Науки о космосе
- Ботаника
- Астрология
- Ветеринария
- История Европы
- География
- Зарубежная публицистика
- О животных
- Шпаргалки
- Разная литература
- Зарубежная литература о культуре и искусстве
- Пословицы, поговорки
- Боевые искусства
- Прочее
- Периодические издания
- Фанфик
- Военное
- Цитаты из афоризмов
- Гиды, путеводители
- Литература 19 века
- Зарубежная образовательная литература
- Военная история
- Кино
- Современная литература
- Военная техника, оружие
- Культура и искусство
- Музыка, музыканты
- Газеты и журналы
- Современная зарубежная литература
- Визуальные искусства
- Отраслевые издания
- Шахматы
- Недвижимость
- Великолепные истории
- Музыка, танцы
- Авто и ПДД
- Изобразительное искусство, фотография
- Истории из жизни
- Готические новеллы
- Начинающие авторы
- Спецслужбы
- Подростковая литература
- Зарубежная прикладная литература
- Религия и духовность
- Старинная литература
- Справочная литература
- Компьютеры и Интернет
- Блог
Основы программирования в Linux - Нейл Мэтью
Шрифт:
Интервал:
Закладка:
Когда select возвращает управление программе, множества дескрипторов будут модифицированы для того, чтобы указать на готовые к чтению или записи или имеющие ошибки дескрипторы. Для их проверки следует использовать макрос FD_ISSET, позволяющий определить, какие дескрипторы требуют внимания. Можно изменить значение timeout для того, чтобы показать время, остающееся до следующего превышения времени ожидания, но такое поведение не задано стандартом X/Open. При превышении времени ожидания все множества дескрипторов будут очищены.
Вызов select возвращает общее количество дескрипторов в модифицированных множествах. В случае сбоя он вернет -1 и установит значение переменной errno, описывающее ошибку. Возможные ошибки — EBADF для неверных дескрипторов, EINTR для возврата из-за прерывания и EINVAL для некорректных значений параметров nfds или timeout.
ПримечаниеНесмотря на то, что Linux модифицирует структуру, на которую указывает timeout, фиксируя оставшееся неиспользованное время, большинство версий UNIX этого не делают. Большая часть существующего программного кода, применяющего функцию select, инициализирует структуру типа timeval и затем продолжает использовать ее без обновления содержимого. В системе Linux этот код может выполняться некорректно, поскольку ОС Linux изменяет структуру timeval при каждом истечении отведенного времени ожидания. Если вы пишете или переносите программный код, использующий функцию select, следует учитывать эту разницу и всегда повторно инициализировать время ожидания. Имейте в виду, что оба подхода корректны, они просто разные!
Выполните упражнение 15.8.
Упражнение 15.8. Функция selectДалее для демонстрации применения функции select приведена программа select.c. Более сложный пример вы увидите чуть позже. Программа читает данные с клавиатуры (стандартный ввод — дескриптор 0) со временем ожидания 2,5 секунды. Данные читаются только тогда, когда ввод готов. Естественно расширить программу, включив в зависимости от характера приложения другие дескрипторы, такие как последовательные каналы (serial lines) и сокеты.
1. Начните как обычно с директив include и объявлений, а затем инициализируйте inputs для обработки ввода с клавиатуры:
#include <sys/types.h>
#include <sys/time.h>
#include <stdio.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <unistd.h>
#include <stdlib.h>
int main() {
char buffer[128];
int result, nread;
fd_set inputs, testfds;
struct timeval timeout;
FD_ZERO(&inputs);
FD_SET(0, &inputs);
2. Подождите ввод из файла stdin в течение максимум 2,5 секунд:
while(1) {
testfds = inputs;
timeout.tv_sec = 2;
timeout.tv_usec = 500000;
result = select(FD_SETSIZE, &testfds, (fd_set *)NULL,
(fd_set*)NULL, &timeout);
3. Спустя это время проверьте result. Если ввода не было, программа выполнит цикл еще раз. Если в нем возникла ошибка, программа завершается:
switch(result) {
case 0:
printf("timeoutn");
break;
case -1:
perror("select");
exit(1);
4. Если во время ожидания у вас наблюдаются некоторые действия, связанные с файловым дескриптором, читайте ввод из stdin и выводите его при каждом получении символа EOL (конец строки), до нажатой комбинации клавиш <Ctrl>+<D>:
default:
if (FD_ISSET(0, &testfds)) {
ioctl(0, FIONREAD, &nread);
if (nread == 0) {
printf("keyboard donen");
exit(0);
}
nread = read(0, buffer, nread);
buffer[nread] = 0;
printf("read %d from keyboard: %s", nread, buffer);
}
break;
}
}
}
Во время выполнения эта программа каждые две с половиной секунды выводит строку timeout. Если вы набираете данные на клавиатуре, она читает файл стандартного ввода и отображает то, что было набрано. В большинстве командных оболочек ввод направляется в программу при нажатии пользователем клавиши <Enter> (или <Return>) или клавиш управляющей последовательности, поэтому программа будет отображать ввод каждый раз, когда вы нажимаете клавишу <Enter>. Учтите, что сама клавиша <Enter> тоже читается и обрабатывается как любой другой символ (попробуйте выполнить ввод без нажатия клавиши, введя ряд символов, за которыми следует комбинация <Ctrl>+<D>).
$ ./select
timeout
hello
read 6 from keyboard: hello
fred
read 5 from keyboard: fred
timeout
^D
keyboard done
$
Как это работает
Программа применяет вызов select для проверки состояния стандартного ввода. За счет корректировки значения времени ожидания программа каждые 2,5 секунды выводит сообщение об истечении времени ожидания. О нем свидетельствует возвращение 0 функцией select. При достижении конца файла дескриптор стандартного ввода помечается флагом как готовый к вводу, но при этом нет символов, предназначенных для считывания.
Множественные клиенты
Ваша простая серверная программа может выиграть от применения select для одновременной обработки множественных клиентов, не прибегая к помощи дочерних процессов. Используя этот метод в реальных приложениях, вы должны следить за тем, чтобы другие клиенты не ждали слишком долго, пока вы обрабатываете первого подключившегося клиента.
Сервер может применять функцию select одновременно к сокету, ожидающему запросы на подключение, и к сокетам клиентских соединений. Как только активность зафиксирована, можно использовать макрос FD_ISSET для проверки в цикле всех возможных файловых дескрипторов и выявления активных среди них.
Если сокет, ожидающий запросов на подключение, готов к вводу, это означает, что клиент пытается подсоединиться, и вы можете вызывать функцию accept без риска блокировки. Если клиентский дескриптор указывает на готовность, это означает, что есть запрос клиента, ждущий, что вы сможете прочесть и обработать его. Чтение 0 байтов означает, что клиентский процесс завершился, и вы можете закрыть сокет и удалить его из множества своих дескрипторов.
Выполните упражнение 15.9.
Упражнение 15.9. Улучшенное клиент-серверное приложение1. В финальный пример программы server5.с вы включите заголовочные файлы sys/time.h и sys/ioctl.h вместо signal.h, использованного в предыдущей программе, и объявите несколько дополнительных переменных для работы с вызовом select:
#include <sys/types.h>
#include <sys/socket.h>
#include <stdio.h>
#include <netinet/in.h>
#include <sys/time.h>
#include <sys/ioctl.h>
#include <unistd.h>
#include <stdlib.h>
int main() {
int server_sockfd, client_sockfd;
int server_len, client_len;
struct sockaddr_in server_address;
struct sockaddr_in client_address;
int result;
fd_set readfds, testfds;
2. Создайте сокет для сервера и присвойте ему имя:
server_sockfd = socket(AF_INET, SOCK_STREAM, 0);
server_address.sin_family = AF_INET;
server_address.sin_addr.s_addr = htonl(INADDR_ANY);
server_address.sin_port = htons(9734);
server_len = sizeof(server_address);
bind(serversockfd, (struct sockaddr *)&server_address, server_len);
3. Создайте очередь запросов на соединение и инициализируйте множество readfds для обработки ввода с сокета server_sockfd:
listen(server_sockfd, 5);
FD_ZERO(&readfds);
FD_SET(server_sockfd, &readfds);
4. Теперь ждите запросы от клиентов. Поскольку вы передали пустой указатель как параметр timeout, не будет наступать истечения времени ожидания. Программа завершится и сообщит об ошибке, если select возвращает значение, меньшее 1.
while(1) {
char ch;
int fd;
int nread;
testfds = readfds;
printf("server waitingn");
result = select(FD_SETSIZE, &testfds, (fd_set *)0,
(fd_set *)0, (struct timeval *)0);
if (result < 1) {
perror("server5");
exit(1);
}
5. После того как вы определили, что есть активность, можно выяснить, какой из дескрипторов активен, проверяя каждый из них по очереди с помощью макроса FD_ISSET:
for (fd = 0; fd < FD_SETSIZE; fd++) {
if (FD_ISSET(fd, &testfds)) {
6. Если зафиксирована активность на server_sockfd, это может быть запрос на новое соединение, и вы добавляете в множество дескрипторов соответствующий client_sockfd:
if (fd == server_sockfd) {
client_len = sizeof(client_address);

