Категории
Самые читаемые
PochitayKnigi » Компьютеры и Интернет » Программное обеспечение » UNIX: разработка сетевых приложений - Уильям Стивенс

UNIX: разработка сетевых приложений - Уильям Стивенс

Читать онлайн UNIX: разработка сетевых приложений - Уильям Стивенс

Шрифт:

-
+

Интервал:

-
+

Закладка:

Сделать
1 ... 166 167 168 169 170 171 172 173 174 ... 263
Перейти на страницу:

40  iovrecv[l].iov_base = inbuff;

41  iovrecv[l].iov_len = inbytes;

1-5 Мы включаем новый заголовочный файл unprtt.h, показанный в листинге 22.8, который определяет структуру rtt_info, содержащую информацию RTT для клиента. Мы определяем одну из этих структур и ряд других переменных.

Определение структур msghdr и структуры hdr

6-10 Мы хотим скрыть от вызывающего процесса добавление порядкового номера и отметки времени в начало каждого пакета. Проще всего использовать для этого функцию writev, записав свой заголовок (структура hdr), за которым следуют данные вызывающего процесса, в виде одной дейтаграммы UDP. Вспомните, что результатом выполнения функции writev на дейтаграммном сокете является отправка одной дейтаграммы. Это проще, чем заставлять вызывающий процесс выделять для нас место в начале буфера, а также быстрее, чем копировать наш заголовок и данные вызывающего процесса в один буфер (под который мы должны выделить память) для каждой функции sendto. Но поскольку мы работаем с UDP и нам необходимо задать адрес получателя, следует использовать возможности, предоставляемые структурой iovec функций sendmsg и recvmsg и отсутствующие в функциях sendto и recvfrom. Вспомните из раздела 14.5, что в некоторых системах доступна более новая структура msghdr, включающая вспомогательные данные (msg_control), тогда как в более старых системах вместо них применяются элементы msg_accright (так называемые права доступа — access rights), расположенные в конце структуры. Чтобы избежать усложнения кода директивами #ifdef для обработки этих различий, мы объявляем две структуры msghdr как static. При этом они инициализируются только нулевыми битами, а затем неиспользованные элементы в конце структур просто игнорируются.

Инициализация при первом вызове

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 {

 5  float    rtt_rtt;    /* последнее измеренное значение RTT в секундах */

 6  float    rtt_srtt;   /* сглаженная оценка RTT в секундах */

 7  float    rtt_rttvar; /* сглаженные средние значения отклонений

                            в секундах */

 8  float    rtt_rto;    /* текущее используемое значение RTO, в секундах */

 9  int      rtt_nrexmt; /* количество повторных передач: 0, 1, 2, ... */

10  uint32_t rtt_base;   /* число секунд, прошедшее после 1.1.1970 в начале */

11 };

12 #define RTT_RXTMIN    2 /* минимальное значение тайм-аута для

                              повторной передачи, в секундах */

13 #define RTT_RXTMAX   60 /* максимальное значение тайм-аута для

                              повторной передачи, в секундах */

14 #define RTT_MAXNREXMT 3 /* максимально допустимое количество

                              повторных передач одной дейтаграммы */

15 /* прототипы функций */

16 void     rtt_debug(struct rtt_info*);

17 void     rtt_init(struct rtt_info*);

18 void     rtt_newpack(struct rtt_info*);

19 int      rtt_start(struct rtt_info*);

20 void     rtt_stop(struct rtt_info*, uint32_t);

21 int      rtt_timeout(struct rtt_info*);

22 uint32_t rtt_ts(struct rtt_info*);

23 extern int rtt_d_flag; /* может быть ненулевым при наличии

                             дополнительной информации */

24 #endif /* _unp_rtt_h */

Структура rtt_info

4-11 Эта структура содержит переменные, необходимые для того, чтобы определить время передачи пакетов между клиентом и сервером. Первые четыре переменных взяты из уравнений, приведенных в начале этого раздела.

12-14 Эти константы определяют минимальный и максимальный тайм-ауты повторной передачи и максимальное число возможных повторных передач.

В листинге 22.9 показан макрос RTT_RTOCALC и первые две из четырех функций RTT.

Листинг 22.9. Макрос RTT_RTOCALC, функции rtt_minmax и rtt_init

//lib/rtt.c

 1 #include "unprtt.h"

 2 int rtt_d_flag = 0; /* отладочный флаг; может быть установлен в

                          ненулевое значение вызывающим процессом */

1 ... 166 167 168 169 170 171 172 173 174 ... 263
Перейти на страницу:
Тут вы можете бесплатно читать книгу UNIX: разработка сетевых приложений - Уильям Стивенс.
Комментарии