Мы работаем с двумя буферами: буфер to содержит данные, направляющиеся из стандартного потока ввода к серверу, а буфер fr — данные, приходящие от сервера в стандартный поток вывода. На рис. 16.1 представлена организация буфера to и указателей в буфере.

UNIX: разработка сетевых приложений img_102.png

Рис. 16.1. Буфер, содержащий данные из стандартного потока ввода, идущие к сокету

Указатель toiptr указывает на следующий байт, в который данные могут быть считаны из стандартного потока ввода. Указатель tooptr указывает на следующий байт, который должен быть записан в сокет. Число байтов, которое может быть считано из стандартного потока ввода, равно &to[MAXLINE] минус toiptr. Как только значение tooptr достигает toiptr, оба указателя переустанавливаются на начало буфера.

На рис. 16.2 показана соответствующая организация буфера fr. В листинге 16.1[1] представлена первая часть функции.

UNIX: разработка сетевых приложений img_103.png

Рис. 16.2. Буфер, содержащий данные из сокета, идущие к стандартному устройству вывода

Листинг 16.1. Функция str_cli: первая часть, инициализация и вызов функции

//nonblock/strclinonb.c

 1 #include "unp.h"

 2 void

 3 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

20 Как и в случае первой версии этой функции, показанной в листинге 6.2, основной цикл функции содержит вызов функции select, за которой следуют отдельные проверки различных интересующих нас условий.

Подготовка интересующих нас дескрипторов

21-30 Оба набора дескрипторов обнуляются и затем в каждом наборе включается не более двух битов. Если мы еще не прочитали конец файла из стандартного потока ввода и есть место как минимум для 1 байта данных в буфере to, то в наборе флагов чтения включается бит, соответствующий стандартному потоку ввода. Если есть место как минимум для 1 байта данных в буфере fr, то в наборе флагов чтения включается бит, соответствующий сокету. Если есть данные для записи в сокет в буфере to, то в наборе флагов записи включается бит, соответствующий сокету. Наконец если в буфере fr есть данные для отправки в стандартный поток вывода, то в наборе флагов записи включается бит, соответствующий этому стандартному потоку.

Вызов функции select

31 Вызывается функция select, ожидающая, когда одно из четырех условий станет истинным. Для этой функции мы не задаем тайм-аута.

Следующая часть нашей функции показана в листинге 16.2. Этот код содержит первые две проверки (из четырех возможных), выполняемые после завершения функции select.

Листинг 16.2. Функция str_cli: вторая часть, чтение из стандартного потока ввода или сокета

//nonblock/strclinonb.c

32   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) {

вернуться

1

Все исходные коды программ, опубликованные в этой книге, вы можете найти по адресу http://www.piter.com.


Перейти на страницу:
Изменить размер шрифта: