KnigaRead.com/
KnigaRead.com » Компьютеры и Интернет » Программное обеспечение » Уильям Стивенс - UNIX: разработка сетевых приложений

Уильям Стивенс - UNIX: разработка сетевых приложений

На нашем сайте KnigaRead.com Вы можете абсолютно бесплатно читать книгу онлайн Уильям Стивенс, "UNIX: разработка сетевых приложений" бесплатно, без регистрации.
Перейти на страницу:

Структура kevent определяется в заголовочном файле <sys/event.h>:

struct kevent {

 uintptr_t ident;  /* идентификатор (например, дескриптор файла) */

 short     filter; /* тип фильтра (например, EVFILT_READ) */

 u_short   flags;  /* флаги действий (например, EV_ADD); */

 u_int     fflags; /* флаги, относящиеся к конкретным фильтрам */

 intptr_t  data;   /* данные, относящиеся к конкретным фильтрам */

 void      uidata; /* непрозрачные пользовательские данные */

};

Действия по смене фильтра и флаговые возвращаемые значения приведены в табл. 14.5.


Таблица 14.5. Флаги для операций kevent

Значение flags Описание Изменяется Возвращается EV_ADD Добавить новое событие, подразумевается по умолчанию, если не указан флаг EV_DISABLE • EV_CLEAR Сброс состояния события после считывания его пользователем • EV_DELETE Удаление события из фильтра • EV_DISABLE Отключение события без удаления его из фильтра • EV_ENABLE Включение отключенного перед этим события • EV_ONESHOT Удаление события после его однократного срабатывания • EV_EOF Достигнут конец файла • EV_ERROR Произошла ошибка, код errno записан в поле data •

Типы фильтров приведены в табл. 14.6.


Таблица 14.6. Типы фильтров

Значение filter Описание EVFILT_AIO События асинхронного ввода-вывода EVFILT_PROC События exit, fork, exec для процесса EVFILT_READ Дескриптор готов для чтения (аналогично select) EVFILT_SIGNAL Описание сигнала EVFILT_TIMER Периодические или одноразовые таймеры EVFILT_VNODE Изменение и удаление файлов EVFILT_WRITE Дескриптор готов для записи (аналогично select)

Перепишем функцию str_cli из листинга 6.2 так, чтобы она использовала kqueue. Результат представлен в листинге 14.8.

Листинг 14.8. Функция str_cli, использующая kqueue

//advio/str_cli_kqueue04.c

 1 #include "unp.h"


 2 void

 3 str_cli(FILE *fp, int sockfd)

 4 {

 5  int kq, i, n, nev, stdineof = 0, isfile;

 6  char buf[MAXLINE];

 7  struct kevent kev[2];

 8  struct timespec ts;

 9  struct stat st;


10  isfile = ((fstat(fileno(fp), &st) 0) &&

11   (st.st_mode & S_IFMT) == S_IFREG);


12  EV_SET(&kev[0], fileno(fp), EVFILT_READ, EV_ADD, 0, 0, NULL);

13  EV_SET(&kev[1], sockfd, EVFILT_READ, EV_ADD, 0, 0, NULL);


14  kq = Kqueue();

15  ts.tv_sec = ts.tv_nsec = 0;

16  Kevent(kq, kev, 2, NULL, 0, &ts);


17  for (;;) {

18   nev = Kevent(kq, NULL, 0, kev, 2, NULL);


19   for (i = 0; i < nev; i++) {

20    if (kev[i].ident == sockfd) { /* сокет готов для чтения */

21     if ((n = Read(sockfd, buf, MAXLINE)) == 0) {

22      if (stdineof == 1)

23       return; /* нормальное завершение*/

24      else

25       err_quit("str_cli: server terminated prematurely");

26     }


27     Write(fileno(stdout), buf, n);

28    }


29    if (kev[i].ident == fileno(fp)) { /* входной поток готов к чтению */

30     n = Read(fileno(fp), buf, MAXLINE);

31     if (n > 0)

32      Writen(sockfd, buf, n);


33     if (n == 0 || (isfile && n == kev[i].data)) {

34      stdineof = 1;

35      Shutdown(sockfd, SHUT_WR); /* отправка FIN */

36      kev[i].flags = EV_DELETE;

37      Kevent(kq, &kev[i], 1, NULL, 0, &ts); /* удаление

                                                 kevent */

38      continue;

39     }

40    }

41   }

42  }

43 }

Проверка, указывает ли дескриптор на файл

10-11 Поведение kqueue при достижении конца файла зависит от того, связан ли данный дескриптор с файлом, каналом или терминалом, поэтому мы вызываем fstat, чтобы убедиться, что мы работаем с файлом. Эти сведения понадобятся позже.

Настройка структур kevent для kqueue

12-13 При помощи макроса EV_SET мы настраиваем две структуры kevent. Обе содержат фильтр событий готовности к чтению (EVFILT_READ) и запрос на добавление этого события к фильтру (EV_ADD).

Создание kqueue и добавление фильтров

14-16 Мы вызываем kqueue, чтобы получить дескриптор kqueue, устанавливаем тайм- аут равным нулю, чтобы сделать вызов kevent неблокируемым, и наконец, вызываем kevent с массивом kevent на месте соответствующего аргумента.

Бесконечный цикл с блокированием в kevent

17-18 Мы входим в бесконечный цикл и блокируемся в kevent. Функции передается пустой список изменений, потому что все интересующие нас события уже зарегистрированы, и нулевой тайм-аут, что позволяет заблокироваться навечно.

Перебор возвращаемых событий в цикле

19 Мы проверяем все возвращаемые события и обрабатываем их последовательно.

Сокет готов для чтения

20-28 Эта часть кода ничем не отличается от листинга 6.2.

Вход готов для чтения

29-40 Код практически аналогичен листингу 6.2 за тем отличием, что нам приходится обрабатывать конец файла, возвращаемый kqueue. Для каналов и терминалов kqueue возвращает событие готовности дескриптора к чтению, подобно select, так что мы можем считать из этого дескриптора символ конца файла. Для файлов kqueue возвращает количество байтов, оставшихся в поле data структуры struct kevent и предполагает, что приложение само определит, когда оно доберется до конца этих данных. Поэтому мы переписываем цикл таким образом, чтобы отправлять данные по сети, если они были считаны из дескриптора. Затем проверяется достижение конца файла: если мы считали нуль байтов или если мы считали все оставшиеся байты из дескриптора файла, значит, это и есть EOF. Еще одно изменение состоит в том, что вместо FD_CLR для удаления дескриптора из набора файлов мы используем флаг EV_DELETE и вызываем kevent для удаления события из фильтра в ядре.

Рекомендации

Новыми интерфейсами следует пользоваться аккуратно. Читайте свежую документацию, относящуюся к конкретному выпуску операционной системы. Интерфейсы часто меняются от одного выпуска к другому, причем таким образом, что заметно это далеко не сразу. Все это продолжается до тех пор, пока поставщики не проработают все детали функционирования новых интерфейсов.

В целом, лучше избегать написания непереносимых программ. Однако для оптимизации ресурсоемких приложений годятся все средства.

14.10. Резюме

Существует три способа установить ограничение времени для операции с сокетом:

■ Использовать функцию alarm и сигнал SIGALRM.

■ Задать предел времени в функции select.

■ Использовать более новые параметры сокета SO_RCVTIMEO и SO_SNDTIMEO.

Первый способ легко использовать, но он включает обработку сигналов и, как показано в разделе 20.5, может привести к ситуации гонок. Использование функции select означает, что блокирование происходит в этой функции (с заданным в ней пределом времени) вместо блокирования в вызове функции read, write или connect. Другая альтернатива — использование новых параметров сокета — также проста в использовании, но предоставляется не всеми реализациями.

Перейти на страницу:
Прокомментировать
Подтвердите что вы не робот:*