Литмир - Электронная Библиотека
Содержание  
A
A

69    } else {

70     fprintf(stderr, "%s: wrote %d bytes to stdout\n",

71      gf_time(), nwritten);

72     froptr += nwritten; /* только что полученное из функции write

                              число */

73     if (froptr == friptr)

74      froptr = friptr - fr; /* назад к началу буфера */

75    }

76   }

77   if (FD_ISSET(sockfd, &wset) && ((n - toiptr - tooptr) > 0)) {

78    if ((nwritten = write(sockfd, tooptr, n)) < 0) {

79     if (errno != EWOULDBLOCK)

80      err_sys("write error to socket");

81    } else {

82     fprintf(stderr, "%s: wrote %d bytes to socket\n",

83      gf_time(), nwritten);

84     tooptr += nwritten; /* только что полученное из функции write

                              число */

85     if (tooptr == toiptr) {

86      toiptr - tooptr = to; /* назад к началу буфера */

87      if (stdineof)

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

89     }

90    }

91   }

92  }

93 }

Запись в стандартный поток вывода с помощью функции write

65-68
 Если есть возможность записи в стандартный поток вывода и число байтов для записи больше нуля, вызывается функция
write
. Если возвращается ошибка
EWOULDBLOCK
, ничего не происходит. Обратите внимание, что это условие возможно, поскольку код в конце предыдущей части функции включает бит в наборе флагов записи для стандартного потока вывода, когда не известно, успешно выполнилась функция
write
или нет.

Успешное выполнение функции write

68-74
 Если функция
write
выполняется успешно,
froptr
увеличивается на число записанных байтов. Если указатель вывода стал равен указателю ввода, оба указателя переустанавливаются на начало буфера.

Запись в сокет с помощью функции write

76-90
 Эта часть кода аналогична коду, только что описанному для записи в стандартный поток вывода. Единственное отличие состоит в том, что когда указатель вывода доходит до указателя ввода, не только оба указателя переустанавливаются в начало буфера, но и появляется возможность отправить серверу сегмент FIN.

Теперь мы проверим работу этой функции и операций неблокируемого ввода-вывода. В листинге 16.4 показана наша функция

gf_time
, вызываемая из функции
str_cli
.

Листинг 16.4. Функция gf_time: возвращение указателя на строку времени

//lib/gf_time.c

 1 #include "unp.h"

 2 #include <time.h>

 3 char*

 4 gf_time(void)

 5 {

 6  struct timeval tv;

 7  static char str[30];

 8  char *ptr;

 9  if (gettimeofday(&tv, NULL) < 0)

10   err_sys("gettimeofday error");

11  ptr = ctime(&tv.tv_sec);

12  strcpy(str, &ptr[11]);

13  /* Fri Sep 13 00:00:00 1986\n\0 */

14  /* 0123456789012345678901234 5 */

15  snprintf(str + 8, sizeof(str) - 8, ".%06ld", tv.tv_usec);

15  return (str);

17 }

Эта функция возвращает строку, содержащую текущее время с точностью до микросекунд, в таком формате:

12:34:56.123456

Здесь специально используется тот же формат, что и для отметок времени, которые выводятся программой

tcpdump
. Обратите внимание, что все вызовы функции
fprintf
в нашей функции
str_cli
записывают данные в стандартный поток сообщений об ошибках, позволяя нам отделить данные стандартного потока вывода (строки, отраженные сервером) от наших диагностических данных. Затем мы можем запустить наш клиент и функцию
tcpdump
, получить эти диагностические данные вместе с результатом функции
tcpdump
и отсортировать вместе два вида выходных данных в порядке их получения. Это позволит нам увидеть, что происходит в нашей программе, и соотнести это с действиями TCP.

Например, сначала мы запускаем функцию

tcpdump
на нашем узле
solaris
, собирая только сегменты TCP, идущие к порту 7 или от него (эхо-сервер), и сохраняем выходные данные в файле, который называется
tcpd
:

solaris % <b>tcpdump -w tcpd tcp and port 7</b>

Затем мы запускаем клиент TCP на этом узле и указываем сервер на узле

linux
:

solaris % <b>tcpcli02 192.168.1.10 &lt; 2000.lines &gt; out 2&gt; diag</b>

Стандартный поток ввода — это файл

2000.lines
, тот же файл, что мы использовали для листинга 6.2. Стандартный поток вывода перенаправляется в файл
out
, а стандартный поток сообщений об ошибках — в файл
diag
. По завершении мы запускаем:

solaris % <b>diff 2000.lines out</b>

чтобы убедиться, что отраженные строки идентичны введенным строкам. Наконец, мы прекращаем выполнение функции

tcpdump
нажатием соответствующей клавиши терминала, после чего выводим записи функции
tcpdump
, сортируя их по времени получения вместе с данными диагностики, полученными от клиента. В листинге 16.5 показана первая часть этого результата.

180
{"b":"225366","o":1}