В листинге 16.3 показана последняя часть нашей функции.
Листинг 16.3. Функция str_cli: третья часть, запись в стандартный поток вывода или сокет
//nonblock/strclinonb.c
65 if (FD_ISSET(STDOUT_FILENO, &wset) && ((n = friptr - froptr) > 0)) {
66 if ((nwritten = write(STDOUT_FILENO, froptr, n)) < 0) {
67 if (errno != EWOULDBLOCK)
68 err_sys("write error to stdout");
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
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 % tcpdump -w tcpd tcp and port 7
Затем мы запускаем клиент TCP на этом узле и указываем сервер на узле linux
:
solaris % tcpcli02 192.168.1.10 < 2000.lines > out 2> diag
Стандартный поток ввода — это файл 2000.lines
, тот же файл, что мы использовали для листинга 6.2. Стандартный поток вывода перенаправляется в файл out
, а стандартный поток сообщений об ошибках — в файл diag
. По завершении мы запускаем:
solaris % diff 2000.lines out
чтобы убедиться, что отраженные строки идентичны введенным строкам. Наконец, мы прекращаем выполнение функции tcpdump
нажатием соответствующей клавиши терминала, после чего выводим записи функции tcpdump
, сортируя их по времени получения вместе с данными диагностики, полученными от клиента. В листинге 16.5 показана первая часть этого результата.
Читать дальше
Конец ознакомительного отрывка
Купить книгу