Инициализация при первом вызове
20-24
При первом вызове нашей функции мы вызываем функцию
rtt_init
.
Заполнение структур msghdr
25-41
Мы заполняем две структуры
msghdr
, используемые для ввода и вывода. Для данного пакета мы увеличиваем на единицу порядковый номер отправки, но не устанавливаем отметку времени отправки, пока пакет не будет отправлен (поскольку он может отправляться повторно, а для каждой повторной передачи требуется текущая отметка времени).
Вторая часть функции вместе с обработчиком сигнала
sig_alarm
показана в листинге 22.7.
Листинг 22.7. Функция dg_send_recv: вторая половина
//rtt/dg_send_rеcv.c
42 Signal(SIGALRM, sig_alrm);
43 rtt_newpack(&rttinfo); /* инициализируем для этого пакета */
44 sendagain:
45 sendhdr.ts = rtt_ts(&rttinfo);
46 Sendmsg(fd, &msgsend, 0);
47 alarm(rtt_start(&rttinfo)); /* вычисляем тайм-аут. запускаем таймер */
48 if (sigsetjmp(jmpbuf, 1) != 0) {
49 if (rtt_timeout(&rttinfо) < 0) {
50 err_msg("dg_send_recv: no response from server, giving up");
51 rttinit = 0; /* повторная инициализация для следующего вызова */
52 errno = ETIMEDOUT;
53 return (-1);
54 }
55 goto sendagain;
56 }
57 do {
58 n = Recvmsg(fd, &msgrecv, 0);
59 } while (n < sizeof(struct hdr) || recvhdr.seq != sendhdr.seq);
60 alarm(0); /* останавливаем таймер SIGALRM */
61 /* вычисляем и записываем новое значение оценки RTT */
62 rtt_stop(&rttinfo, rtt_ts(&rttinfo) — recvhdr.ts);
63 return (n - sizeof(struct hdr)); /* возвращаем размер полученной
дейтаграммы */
64 }
65 static void
66 sig_alrm(int signo)
67 {
68 siglongjmp(jmpbuf, 1);
69 }
Установка обработчика сигналов
42-43
Для сигнала
SIGALRM
устанавливается обработчик сигналов, а функция
rtt_newpack
устанавливает счетчик повторных передач в нуль.
Отправка дейтаграммы
45-47
Функция
rtt_ts
получает текущую отметку времени. Отметка времени хранится в структуре
hdr
, которая добавляется к данным пользователя. Одиночная дейтаграмма UDP отправляется функцией
sendmsg
. Функция
rtt_start
возвращает количество секунд для этого тайм-аута, а сигнал
SIGALRM
контролируется функцией
alarm
.
Установка буфера перехода
48
Мы устанавливаем буфер перехода для нашего обработчика сигналов с помощью функции sigsetjmp. Мы ждем прихода следующей дейтаграммы, вызывая функцию
recvmsg
. (Совместное использование функций
sigsetjmp
и
siglongjmp
вместе с сигналом
SIGALRM
мы обсуждали применительно к листингу 20.5.) Если время таймера истекает, функция
sigsetjmp
возвращает 1.
Обработка тайм-аута
49-55
Когда возникает тайм-аут, функция
rtt_timeout
вычисляет следующее значение RTO (используя экспоненциальное смещение) и возвращает -1, если нужно прекратить попытки передачи дейтаграммы, или 0, если нужно выполнить очередную повторную передачу. Когда мы прекращаем попытки, мы присваиваем переменной
errno
значение
ETIMEDOUT
и возвращаемся в вызывающую функцию.
Вызов функции recvmsg, сравнение порядковых номеров
57-59
Мы ждем прихода дейтаграммы, вызывая функцию
recvmsg
. Длина полученной дейтаграммы не должна быть меньше размера структуры
hdr
, а ее порядковый номер должен совпадать с порядковым номером запроса, ответом на который предположительно является эта дейтаграмма. Если при сравнении хотя бы одно из этих условий не выполняется, функция
recvmsg
вызывается снова.
Выключение таймера и обновление показателей RTT
60-62
Когда приходит ожидаемый ответ, функция
alarm
отключается, а функция
rtt_stop
обновляет оценочное значение RTT. Функция
rtt_ts
возвращает текущую отметку времени, и отметка времени из полученной дейтаграммы вычитается из текущей отметки, что дает в результате RTT.
Обработчик сигнала SIGALRM
65-69
Вызывается функция
siglongjmp
, результатом выполнения которой является то, что функция
sigsetjmp
в
dg_send_recv
возвращает 1.
Теперь мы рассмотрим различные функции RTT, которые вызывались нашей функцией
dg_send_recv
. В листинге 22.8 показан заголовочный файл
unprtt.h
.
Листинг 22.8. Заголовочный файл unprtt.h
//lib/unprtt.h
1 #ifndef __unp_rtt_h
2 #define __unp_rtt_h
3 #include "unp.h"
4 struct rtt_info {