UNIX: разработка сетевых приложений
Мы работаем с двумя буферами: буфер to содержит данные, направляющиеся из стандартного потока ввода к серверу, а буфер
— данные, приходящие от сервера в стандартный поток вывода. На рис. 16.1 представлена организация буфераfrи указателей в буфере.toРис. 16.1. Буфер, содержащий данные из стандартного потока ввода, идущие к сокету
Указатель
указывает на следующий байт, в который данные могут быть считаны из стандартного потока ввода. Указательtoiptrуказывает на следующий байт, который должен быть записан в сокет. Число байтов, которое может быть считано из стандартного потока ввода, равноtooptrминус&to[MAXLINE]. Как только значениеtoiptrдостигаетtooptr, оба указателя переустанавливаются на начало буфера.toiptrНа рис. 16.2 показана соответствующая организация буфера
. В листинге 16.1 [1] представлена первая часть функции.frРис. 16.2. Буфер, содержащий данные из сокета, идущие к стандартному устройству вывода
Листинг 16.1. Функция str_cli: первая часть, инициализация и вызов функции
//nonblock/strclinonb.c1 #include "unp.h"2 void3 str_cli(FILE *fp, int sockfd)4 {5 int maxfdp1, val, stdineof;6 ssize_t n, nwritten;7 fd_set rset, wset;8 char to[MAXLINE], fr[MAXLINE];9 char *toiptr, *tooptr, *friptr, *froptr;10 val = Fcntl(sockfd, F_GETFL, 0);11 Fcntl(sockfd, F_SETFL, val | O_NONBLOCK);12 val = Fcntl(STDIN_FILENO, F_SETFL, 0);13 Fcntl(STDIN_FILENO, F_SETFL, val | O_NONBLOCK);14 val = Fcntl(STDOUT_FILENO, F_SETFL, 0);15 Fcntl(STDOUT_FILENO, F_SETFL, val | O_NONBLOCK);16 toiptr = tooptr = to; /* инициализация указателей буфера */17 friptr = froptr = fr;18 stdineof = 0;19 maxfdp1 = max(max(STDIN_FILENO, STDOUT_FILENO), sockfd) + 1;20 for (;;) {21 FD_ZERO(&rset);22 FD_ZERO(&wset);23 if (stdineof == 0 && toiptr < &to[MAXLINE])24 FD_SET(STDIN_FILENO, &rset); /* чтение из стандартного потокаввода */25 if (friptr < &fr[MAXLINE])26 FD_SET(sockfd, &rset); /* чтение из сокета */27 if (tooptr != toiptr)28 FD_SET(sockfd, &wset); /* данные для записи в сокет */29 if (froptr != friptr)30 FD_SET(STDOUT_FILENO, &wset); /* данные для записи в стандартныйпоток вывода */31 Select(maxfdp1, &rset, &wset, NULL, NULL);Установка неблокируемых дескрипторовВсе три дескриптора делаются неблокируемыми при помощи функции10-15: сокет в направлении к серверу и от сервера, стандартный поток ввода и стандартный поток вывода.fcntlИнициализация указателей буфераИнициализируются указатели в двух буферах и вычисляется максимальный дескриптор. Это значение, увеличенное на единицу, будет использоваться в качестве первого аргумента функции16-19.selectОсновной цикл: подготовка к вызову функции selectКак и в случае первой версии этой функции, показанной в листинге 6.2, основной цикл функции содержит вызов функции20, за которой следуют отдельные проверки различных интересующих нас условий.selectПодготовка интересующих нас дескрипторовОба набора дескрипторов обнуляются и затем в каждом наборе включается не более двух битов. Если мы еще не прочитали конец файла из стандартного потока ввода и есть место как минимум для 1 байта данных в буфере21-30, то в наборе флагов чтения включается бит, соответствующий стандартному потоку ввода. Если есть место как минимум для 1 байта данных в буфереto, то в наборе флагов чтения включается бит, соответствующий сокету. Если есть данные для записи в сокет в буфереfr, то в наборе флагов записи включается бит, соответствующий сокету. Наконец если в буфереtoесть данные для отправки в стандартный поток вывода, то в наборе флагов записи включается бит, соответствующий этому стандартному потоку.frВызов функции selectВызывается функция31, ожидающая, когда одно из четырех условий станет истинным. Для этой функции мы не задаем тайм-аута.selectСледующая часть нашей функции показана в листинге 16.2. Этот код содержит первые две проверки (из четырех возможных), выполняемые после завершения функции
.selectЛистинг 16.2. Функция str_cli: вторая часть, чтение из стандартного потока ввода или сокета
//nonblock/strclinonb.c32 if (FD_ISSET(STDIN_FILENO, &rset)) {33 if ((n = read(STDIN_FILENO, toiptr, &to[MAXLINE] - toiptr)) < 0) {34 if (errno != EWOULDBLOCK)35 err_sys("read error on stdin");36 } else if (n == 0) {

