UNIX: разработка сетевых приложений
Часть 336 из 399 Информация о книге
Листинг 29.6. Функция test_udp: отправка запросов и считывание ответов
//udpcksum/udpcksum.c12 void13 test_udp(void)14 {15 volatile int nsent = 0, timeout = 3;16 struct udpiphdr *ui;17 Signal(SIGALRM, sig_alrm);18 if (sigsetjmp(jmpbuf, 1)) {19 if (nsent >= 3)20 err_quit("no response");21 printf("timeout\n");22 timeout *= 2; /* геометрическая прогрессия: 3, 6, 12 */23 }24 canjump = 1; /* siglongjmp разрешен */25 send_dns_query();26 nsent++;27 alarm(timeout);28 ui = udp_read();29 canjump = 0;30 alarm(0);31 if (ui->ui_sum == 0)32 printf("UDP checksums off\n");33 else34 printf("UDP checksums on\n");35 if (verbose)36 printf("received UDP checksum = %x\n", ntohs(ui->ui_sum));37 }Переменные volatileНам нужно, чтобы две динамические локальные переменные15иnsentсохраняли свои значения после возвращенияtimeoutиз обработчика сигнала в нашу функцию. Реализация допускает восстановление значений динамических локальных переменных, предшествовавших вызову функцииsiglongjmp[110, с. 178], но добавление спецификатораsigsetjumpпредотвращает это восстановление.volatileУстановление обработчика сигналов и буфера переходаДля сигнала15-16устанавливается обработчик сигнала, а функцияSIGALRMустанавливает буфер перехода для функцииsigsetjmp. (Эти две функции подробно описаны в разделе 10.15 [110].) Значение 1 во втором аргументе функцииsiglongjmpуказывает, что требуется сохранить текущую маску сигнала, так как мы будем вызывать функциюsigsetjmpиз нашего обработчика сигнала.siglongjmpФункция siglongjmpЭтот фрагмент кода выполняется, только когда функция19-23вызывается из нашего обработчика сигнала. Вызов указывает на возникновение условий, при которых мы входим в состояние ожидания: мы отправили запрос, на который не пришло никакого ответа. Если после того, как мы отправим три запроса, ответа не будет, мы прекращаем выполнение кода. По истечении времени ожидания, отведенного на получение ответа, мы выводим соответствующее сообщение и увеличиваем значение времени ожидания в два раза, то есть задаем экспоненциальное смещение (exponential backoff), которое также описано в разделе 20.5. Первое значение времени ожидания равно 3 с, затем — 6 с и 12 с.siglongjmpПричина, по которой в этом примере мы используем функции
иsigsetjmp, вместо того чтобы просто перехватывать ошибкуsiglongjmp(как мы поступили в листинге 14.1), заключается в том, что библиотечные функции захвата пакетов (которые вызываются из нашей функцииEINTR) заново запускают операцию чтения в случае возвращения ошибкиudp_read. Поскольку мы не хотим модифицировать библиотечные функции, единственным решением для нас является перехватывание сигналаEINTRи выполнение нелокального перехода (оператораSIGALRM), который возвращает управление в наш код, а не в библиотечную функцию.gotoОтправка запроса DNS и считывание ответаФункция25-26(см. листинг 29.8) отправляет запрос DNS на сервер имен. Функцияsend_dns_queryсчитывает ответ. Мы вызываем функциюdns_readдля предотвращения «вечной» блокировки функцииalarm. Если истекает заданное (в секундах) время ожидания, генерируется сигналread, и наш обработчик сигнала вызывает функциюSIGALRM.siglongjmpАнализ полученной контрольной суммы UDPЕсли значение полученной контрольной суммы UDP равно нулю, это значит, что сервер не вычислил и не отправил контрольную сумму.27-32В листинге 29.7 показана наша функция
— обработчик сигналаsig_alrm.SIGALRMЛистинг 29.7. Функция sig_alrm: обработка сигнала SIGALRM
//udpcksum/udpcksum.c1 #include "udpcksum.h"2 #include <setjmp.h>3 static sigjmp_buf jmpbuf;4 static int canjump;5 void6 sig_alrm(int signo)7 {8 if (canjump == 0)9 return;10 siglongjmp(jmpbuf, 1);11 }Флаг8-10был установлен в листинге 29.6 после инициализации буфера перехода функциейcanjump. Если флаг был установлен, в результате вызова функцииsigsetjmpуправление осуществляется таким образом, как если бы функцияsiglongjmpиз листинга 29.6 возвратила бы значение 1.sigsetjmpВ листинге 29.8 показана функция
, посылающая запрос UDP на сервер DNS. Эта функция формирует запрос DNS.send_dns_query