функция возвращает строку, содержащую текущее время с точностью до микросекунд, в таком формате:
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 показана первая часть этого результата.
Листинг 16.5. Отсортированный вывод функции tcpdump и данных диагностики
solaris % tcpdump -r tcpd -N | sort diag -
10:18:34.486392 solaris.33621 > linux.echo: S 1802738644:1802738644(0) win 8760 <mss 1460>
10:18:34.643524: read 4096 bytes from stdin 10:18:34.667305. read 2636 bytes from socket
10:18:34.670324 solaris.33621 > linux echo: . ack 7605 win 8760
10:18:34.672221 linux.echo > solaris.33621: P 7605.8193(588) ack 8193 win 8760
10:18:34.691039: wrote 2636 bytes to stdout
Мы
удалили записи (
DF
) из сегментов, отправленных Solaris, означающие, что устанавливается бит DF (он используется для определения величины транспортной MTU).
Используя этот вывод, мы можем нарисовать временную диаграмму происходящих событий (рис. 16.3). На этой диаграмме представлены события в различные моменты времени, причем ориентация диаграммы такова, что более поздние события расположены ниже на странице.
Рис. 16.3. Временная диаграмма событий для примера неблокируемого ввода
На этом рисунке мы не показываем сегменты ACK. Также помните, что если программа выводит сообщение
wrote N bytes to stdout
(записано Nбайт в стандартное устройство вывода), это означает, что завершилась функция
write
, возможно, заставившая TCP отправить один или более сегментов данных.
По этому рисунку мы можем проследить динамику обмена между клиентом и сервером. Использование неблокируемого ввода-вывода позволяет программе использовать преимущество этой динамики, считывая или записывая данные, когда операция ввода или вывода может иметь место. Ядро сообщает нам, когда может произойти операция ввода-вывода, при помощи функции