UNIX: разработка сетевых приложений
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Эта часть кода аналогична коду, только что описанному для записи в стандартный поток вывода. Единственное отличие состоит в том, что когда указатель вывода доходит до указателя ввода, не только оба указателя переустанавливаются в начало буфера, но и появляется возможность отправить серверу сегмент FIN.76-90Теперь мы проверим работу этой функции и операций неблокируемого ввода-вывода. В листинге 16.4 показана наша функция
, вызываемая из функцииgf_time.str_cliЛистинг 16.4. Функция gf_time: возвращение указателя на строку времени
//lib/gf_time.c1 #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и отсортировать вместе два вида выходных данных в порядке их получения. Это позволит нам увидеть, что происходит в нашей программе, и соотнести это с действиями TCP.tcpdumpНапример, сначала мы запускаем функцию
на нашем узлеtcpdump, собирая только сегменты TCP, идущие к порту 7 или от него (эхо-сервер), и сохраняем выходные данные в файле, который называетсяsolaris:tcpdsolaris % <b>tcpdump -w tcpd tcp and port 7</b>Затем мы запускаем клиент TCP на этом узле и указываем сервер на узле
:linuxsolaris % <b>tcpcli02 192.168.1.10 < 2000.lines > out 2> diag</b>Стандартный поток ввода — это файл
, тот же файл, что мы использовали для листинга 6.2. Стандартный поток вывода перенаправляется в файл2000.lines, а стандартный поток сообщений об ошибках — в файлout. По завершении мы запускаем:diagsolaris % <b>diff 2000.lines out</b>чтобы убедиться, что отраженные строки идентичны введенным строкам. Наконец, мы прекращаем выполнение функции
нажатием соответствующей клавиши терминала, после чего выводим записи функцииtcpdump, сортируя их по времени получения вместе с данными диагностики, полученными от клиента. В листинге 16.5 показана первая часть этого результата.tcpdump