UNIX: разработка сетевых приложений
Многие реализации имеют объявления, аналогичные приведенному ниже, которое взято из заголовочного файла 4.4BSD
:<sys/types.h>/*Значение FD_SETSIZE может быть определено пользователем,но заданное здесь по умолчаниюявляется достаточным в большинстве случаев.*/#ifndef FD_SETSIZE#define FD_SETSIZE 256#endifИсходя из этого комментария, можно подумать, что если перед подключением этого заголовочного файла присвоить
значение, превышающее 256, то увеличится размер наборов дескрипторов, используемых функциейFD_SETSIZE. К сожалению, обычно это не действует.selectПРИМЕЧАНИЕЧтобы понять, в чем дело, обратите внимание, что на рис. 16.53 [128] объявляются три набора дескрипторов внутри ядра, а в качестве верхнего предела используется определенное в ядре значение FD_SETSIZE. Единственный способ увеличить размер наборов дескрипторов — это увеличить значение FD_SETSIZE и затем перекомпилировать ядро. Изменения значения без перекомпиляции ядра недостаточно.
Некоторые производители изменяют свои реализации функции
, с тем чтобы позволить процессу задавать значениеselect, превышающее значение по умолчанию. BSD/OS также изменила реализацию ядра, чтобы допустить большие наборы дескрипторов, кроме того, в ней добавлено четыре новых макроопределенияFD_SETSIZEдля динамического размещения больших наборов дескрипторов в памяти и для работы с ними. Однако с точки зрения переносимости не стоит злоупотреблять использованием больших наборов дескрипторов.FD_<i>xxx</i>6.4. Функция str_cli (продолжение)
Теперь мы можем переписать нашу функцию
, представленную в разделе 5.5 (на этот раз используя функциюstr_cli), таким образом, чтобы мы получали уведомление, как только завершится процесс сервера. Проблема с предыдущей версией состояла в том, что процесс мог оказаться заблокированным в вызове функцииselect, когда что-то происходило на сокете. Наша новая версия этой функции вместо этого блокируется в вызове функцииfgets, ожидая готовности для чтения либо стандартного потока ввода, либо сокета. На рис. 6.7 показаны различные условия, обрабатываемые с помощью вызова функцииselect.selectРис. 6.7. Условия, обрабатываемые функцией select в вызове функции str_cli
Сокет обрабатывает три условия:
1. Если протокол TCP собеседника отправляет данные, сокет становится готовым для чтения, и функция
возвращает положительное значение (то есть число байтов данных).read2. Если протокол TCP собеседника отправляет сегмент FIN (процесс завершается), сокет становится готовым для чтения, и функция
возвращает нуль (признак конца файла).read3. Если TCP собеседника отправляет RST (узел вышел из строя и перезагрузился), сокет становится готовым для чтения, и функция
возвращает -1, а переменнаяreadсодержит код соответствующей ошибки.errnoВ листинге 6.1 [1] представлен исходный код этой версии функции.
Листинг 6.1. Реализация функции str_cli с использованием функции select (усовершенствованный вариант находится в листинге 6.2)
//select/strcliselect01.c1 #include "unp.h"2 void3 str_cli(FILE *fp, int sockfd)4 {5 int maxfdp1;6 fd_set rset;7 char sendline[MAXLINE], recvline[MAXLINE];8 FD_ZERO(&rset);9 for (;;) {10 FD_SET(fileno(fp), &rset);11 FD_SET(sockfd, &rset);12 maxfdp1 = max(fileno(fp), sockfd) + 1;13 Select(maxfdp1, &rset, NULL, NULL, NULL);14 if (FD_ISSET(sockfd, &rset)) { /* сокет готов для чтения */15 if (Readline(sockfd, recvline, MAXLINE) == 0)16 err_quit("str_cli: server terminated prematurely");17 Fputs(recvline, stdout);18 }19 if (FD_ISSET(fileno(fp), &rset)) { /* входное устройство готово длячтения */20 if (Fgets(sendline, MAXLINE, fp) == NULL)21 return; /* все сделано */22 Writen(sockfd, sendline, strlen(sendline));23 }24 }25 }Вызов функции selectНам нужен только один набор дескрипторов — для проверки готовности сокета для чтения. Этот набор дескрипторов инициализируется макросом8-13, после чего с помощью макросаFD_ZEROустанавливаются два бита: бит, соответствующий указателю файлаFD_SETстандартного потока ввода-вывода, и бит, соответствующий дескриптору сокетаfp. Функцияsockfdпреобразует указатель файла стандартного потока ввода-вывода в соответствующий ему дескриптор. Функцияfileno(а такжеselect) работает только с дескрипторами.pollФункция
вызывается после определения максимального из двух дескрипторов. В этом вызове указатель на набор дескрипторов для записи и указатель на набор дескрипторов с исключениями являются пустыми. Последний аргумент (ограничение по времени) также является пустым указателем, поскольку мы хотим, чтобы процесс был блокирован, пока не будут готовы данные для чтения.selectОбработка сокета, готового для чтения
