UNIX: разработка сетевых приложений
Часть 250 из 399 Информация о книге
Инициализация при первом вызовеПри первом вызове нашей функции мы вызываем функцию20-24.rtt_initЗаполнение структур msghdrМы заполняем две структуры25-41, используемые для ввода и вывода. Для данного пакета мы увеличиваем на единицу порядковый номер отправки, но не устанавливаем отметку времени отправки, пока пакет не будет отправлен (поскольку он может отправляться повторно, а для каждой повторной передачи требуется текущая отметка времени).msghdrВторая часть функции вместе с обработчиком сигнала
показана в листинге 22.7.sig_alarmЛистинг 22.7. Функция dg_send_recv: вторая половина
//rtt/dg_send_rеcv.c42 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 void66 sig_alrm(int signo)67 {68 siglongjmp(jmpbuf, 1);69 }Установка обработчика сигналовДля сигнала42-43устанавливается обработчик сигналов, а функцияSIGALRMустанавливает счетчик повторных передач в нуль.rtt_newpackОтправка дейтаграммыФункция45-47получает текущую отметку времени. Отметка времени хранится в структуреrtt_ts, которая добавляется к данным пользователя. Одиночная дейтаграмма UDP отправляется функциейhdr. Функцияsendmsgвозвращает количество секунд для этого тайм-аута, а сигналrtt_startконтролируется функциейSIGALRM.alarmУстановка буфера переходаМы устанавливаем буфер перехода для нашего обработчика сигналов с помощью функции sigsetjmp. Мы ждем прихода следующей дейтаграммы, вызывая функцию48. (Совместное использование функцийrecvmsgиsigsetjmpвместе с сигналомsiglongjmpмы обсуждали применительно к листингу 20.5.) Если время таймера истекает, функцияSIGALRMвозвращает 1.sigsetjmpОбработка тайм-аутаКогда возникает тайм-аут, функция49-55вычисляет следующее значение RTO (используя экспоненциальное смещение) и возвращает -1, если нужно прекратить попытки передачи дейтаграммы, или 0, если нужно выполнить очередную повторную передачу. Когда мы прекращаем попытки, мы присваиваем переменнойrtt_timeoutзначениеerrnoи возвращаемся в вызывающую функцию.ETIMEDOUTВызов функции recvmsg, сравнение порядковых номеровМы ждем прихода дейтаграммы, вызывая функцию57-59. Длина полученной дейтаграммы не должна быть меньше размера структурыrecvmsg, а ее порядковый номер должен совпадать с порядковым номером запроса, ответом на который предположительно является эта дейтаграмма. Если при сравнении хотя бы одно из этих условий не выполняется, функцияhdrвызывается снова.recvmsgВыключение таймера и обновление показателей RTTКогда приходит ожидаемый ответ, функция60-62отключается, а функцияalarmобновляет оценочное значение RTT. Функцияrtt_stopвозвращает текущую отметку времени, и отметка времени из полученной дейтаграммы вычитается из текущей отметки, что дает в результате RTT.rtt_tsОбработчик сигнала SIGALRMВызывается функция65-69, результатом выполнения которой является то, что функцияsiglongjmpвsigsetjmpвозвращает 1.dg_send_recvТеперь мы рассмотрим различные функции RTT, которые вызывались нашей функцией
. В листинге 22.8 показан заголовочный файлdg_send_recv.unprtt.hЛистинг 22.8. Заголовочный файл unprtt.h
//lib/unprtt.h1 #ifndef __unp_rtt_h2 #define __unp_rtt_h3 #include "unp.h"4 struct rtt_info {