Чтение онлайн

ЖАНРЫ

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

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

37 fprintf(stderr, "%s: EOF on stdin\n", gf_time);

38 stdineof = 1; /* с stdin все сделано */

39 if (tooptr == toiptr)

40 Shutdown(sockfd, SHUT_WR); /* отсылаем FIN */

41 } else {

42 fprintf(stderr, "%s: read %d bytes from stdin\n", gf_time,

43 n);

44 toiptr += n; /*
только что полученное из функции read число */

45 FD_SET(sockfd, &wset); /* включаем бит в наборе чтения */

46 }

47 }

48 if (FD_ISSET(sockfd, &rset)) {

49 if ((n = read(sockfd, friptr, &fr[MAXLINE] - friptr)) < 0) {

50 if (errno != EWOULDBLOCK)

51 err_sys("read error on socket");

52 } else if (n == 0) {

53 fprintf(stderr, "%s: EOF on socket\n", gf_time);

54 if (stdineof)

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

56 else

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

58 } else {

59 fprintf(stderr, "%s: read %d bytes from socket\n",

60 gf_time, n);

61 friptr += n; /* только что полученное из функции read число */

62 FD_SЕТ(STDOUT_FILЕNO, &wset); /* включаем бит в наборе

чтения */

63 }

64 }

Чтение из стандартного потока ввода с помощью функции read

32-33
Если стандартный поток ввода готов для чтения, мы вызываем функцию
read
. Третий ее аргумент — это количество свободного места в буфере
to
.

Обработка ошибки

34-35
Если происходит ошибка
EWOULDBLOCK
, мы ничего не предпринимаем. Обычно эта ситуация — когда функция
select
сообщает нам о том, что дескриптор готов для чтения, а функция read возвращает ошибку
EWOULDBLOCK
— не должна возникать, но тем не менее мы ее обрабатываем.

Возвращение конца файла функцией read

36-40
Если функция
read
возвращает нуль, мы закончили со стандартным потоком ввода. Флаг
stdineof
установлен. Если в буфере to больше нет данных для отправки (
tooptr
равно
toiptr
), функция
shutdown
отправляет серверу сегмент FIN. Если в буфере
to
еще есть данные для отправки, сегмент FIN не может быть отправлен до тех пор, пока содержимое буфера не будет записано в сокет.

Поделиться с друзьями: