UNIX: разработка сетевых приложений
1. Полная буферизация (fully buffered) означает, что ввод-вывод имеет место, только когда буфер заполнен, процесс явно вызывает функцию
или процесс завершается посредством вызова функцииfflush. Обычный размер стандартного буфера ввода-вывода — 8192 байта.exit2. Буферизация по строкам (line buffered) означает, что ввод-вывод имеет место, только когда встречается символ перевода строки, процесс вызывает функцию
или процесс завершается вызовом функцииfflush.exit3. Отсутствие буферизации (unbuffered) означает, что ввод-вывод имеет место каждый раз, когда вызывается функция стандартного ввода-вывода.
Большинство реализаций Unix стандартной библиотеки ввода-вывода используют следующие правила:
■ Стандартный поток ошибок никогда не буферизуется.
■ Стандартные потоки ввода и вывода буферизованы полностью, если они не подключены к терминальному устройству, в противном случае они буферизуются по строкам.
■ Все остальные потоки тоже буферизованы полностью, если они не подключены к терминалу, в случае чего они буферизованы по строкам.
Поскольку сокет не является терминальным устройством, проблема, отмеченная с нашей функцией
в листинге 14.6, заключается в том, что поток вывода (str_echo) полностью буферизован. Есть два решения: мы можем сделать поток вывода буферизованным по строкам при помощи вызова функцииfpotлибо заставить каждую отраженную строку выводиться при помощи вызова функцииsetvbufпосле каждого вызова функцииfflush. Применение любого из этих изменений скорректирует поведение нашей функцииfputs. На практике оба варианта чреваты ошибками и могут плохо взаимодействовать с алгоритмом Нагла. В большинстве случаев оптимальным решением будет отказаться от использования стандартной библиотеки ввода-вывода для сокетов и работать с буферами, а не со строками (см. раздел 3.9). Использование стандартных функций ввода-вывода имеет смысл в тех случаях, когда потенциальный выигрыш перевешивает затруднения.str_echoПРИМЕЧАНИЕБудьте осторожны — некоторые реализации стандартной библиотеки ввода-вывода все еще вызывают проблемы при работе с дескрипторами, большими 255. Эта проблема может возникнуть с сетевыми серверами, обрабатывающими множество дескрипторов. Проверьте определение структуры FILE в вашем заголовочном файле <stdio.h>, чтобы увидеть, к какому типу переменных относится дескриптор.
14.9. Расширенный опрос
В начале этой главы мы рассказывали о способах установки таймеров для операций с сокетами. Во многих операционных системах для этого существуют функции
иpoll, которые были описаны в главе 6. Ни один из этих методов еще не стандартизован POSIX, поэтому между реализациями существуют определенные различия. Код, использующий подобные механизмы, должен считаться непереносимым. Мы рассмотрим два механизма, прочие весьма похожи на них.selectИнтерфейс /dev/poll
В Solaris имеется специальный файл
, с помощью которого можно опрашивать большее количество дескрипторов файлов. Проблема/dev/pollиselectсостоит в том, что список дескрипторов приходится передавать при каждом вызове. Устройство опроса поддерживает информацию о состоянии между вызовами, так что программа может подготовить список подлежащих опросу дескрипторов, а потом спокойно зациклиться в опросе и не заполнять список каждый раз.pollПосле открытия
программа должна инициализировать массив структур/dev/poll(тех же, которые используются функциейpollfd, но в этом случае полеpollне используется). Затем массив передается ядру вызовомrevents(структура записывается непосредственно вwrite). После этого программа может вызывать/dev/pollи ждать событий. При вызовеioctl DP_POLLпередается следующая структура:ioctlstruct dvpoll {struct pollfd* dp_fds;int dp_nfds;int dp_timeout;};Поле
указывает на буфер, используемый для хранения массива структурdp_fds, возвращаемых вызовомpollfd. Полеioctlзадает размер буфера. Вызовdp_nfdsблокируется до появления интересующих программу событий на любом из опрашиваемых дескрипторов, или до прохожденияioctlмиллисекунд. При нулевом значении тайм-аута функцияdp_timeoutвозвращается немедленно (то есть данный способ может использоваться для реализации неблокируемых сокетов). Тайм-аут, равный -1, означает неопределенно долгое ожидание.ioctlИзмененный код функции
, переписанной из листинга 6.2 с использованиемstr_cli, приведен в листинге 14.7./dev/pollЛистинг 14.7. Функция str_cli, использующая /dev/poll
//advio/str_cli_poll03.c1 #include "unp.h"2 #include <sys/devpoll.h>3 void4 str_cli(FILE *fp, int sockfd)5 {6 int stdineof;7 char buf[MAXLINE];8 int n;9 int wfd;10 struct pollfd pollfd[2];11 struct dvpoll dopoll;12 int i;13 int result;14 wfd = Open("/dev/poll", O_RDWR, 0);15 pollfd[0].fd = fileno(fp);16 pollfd[0].events = POLLIN;17 pollfd[0].revents = 0;18 pollfd[1].fd = sockfd;19 pollfd[1].events = POLLIN;20 pollfd[1].revents = 0;21 Write(wfd, pollfd, sizeof(struct pollfd) * 2);