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

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

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

Шрифт:

-
+

Интервал:

-
+

Закладка:

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

 3 /* Вычисление значения RTO на основе текущих значений:

 4  * сглаженное оценочное значение RTT + четырежды сглаженная

 5  * величина отклонения.

 6  */

 7 #define RTI_RTOCALC(ptr) ((ptr)->rtt_srtt + (4.0 * (ptr)->rtt_rttvar))

 8 static float

 9 rtt_minmax(float rto)

10 {

11  if (rto < RTT_RXTMIN)

12   rto = RTT_RXTMIN;

13  else if (rto > RTT_RXTMAX)

14   rto = RTT_RXTMAX;

15  return (rto);

16 }

17 void

18 rtt_init(struct rtt_info *ptr)

19 {

20  struct timeval tv;

21  Gettimeofday(&tv, NULL);

22  ptr->rtt_base = tv.tv_sec; /* количество секунд, прошедших с 1.1.1970 */

23  ptr->rtt_rtt = 0;

24  ptr->rtt_srtt = 0;

25  ptr->rtt_rttvar = 0.75;

26  ptr->rtt_rto = rtt_minmax(RTT_RTOCALC(ptr));

27  /* первое RTO (srtt + (4 * rttvar)) = 3 с */

28 }

3-7 Макрос вычисляет RTO как сумму оценочной величины RTT и оценочной величины среднего отклонения, умноженной на четыре.

8-16 Функция rtt_minmax проверяет, что RTO находится между верхним и нижним пределами, заданными в заголовочном файле unprtt.h.

17-28 Функция rtt_init вызывается функцией dg_send_recv при первой отправке пакета. Функция gettimeofday возвращает текущее время и дату в той же структуре timeval, которую мы видели в функции select (см. раздел 6.3). Мы сохраняем только текущее количество секунд с момента начала эпохи Unix, то есть с 00:00:00 1 января 1970 года (UTC). Измеряемое значение RTT обнуляется, а сглаженная оценка RTT и среднее отклонение принимают соответственно значение 0 и 0,75, в результате чего начальное RTO равно 3 с (4×0,75).

В листинге 22.10 показаны следующие три функции RTT.

Листинг 22.10. Функции rtt_ts, rtt_newpack и rtt_start

//lib/rtt.c

34 uint32_t

35 rtt_ts(struct rtt_info *ptr)

36 {

37  uint32_t ts;

38  struct timeval tv;

39  Gettimeofday(&tv, NULL);

40  ts = ((tv.tv_sec - ptr->rtt_base) * 1000) + (tv.tv_usec / 1000);

41  return (ts);

42 }

43 void

44 rtt_newpack(struct rtt_info *ptr)

45 {

46  ptr->rtt_nrexmt = 0;

47 }

48 int

49 rtt_start(struct rtt_info *ptr)

50 {

51  return ((int)(ptr->rtt_rto + 0.5)); /* округляем float до int */

52  /* возвращенное значение может быть использовано как аргумент

       alarm(rtt_start(&fоо)) */

53 }

34-42 Функция rtt_ts возвращает текущую отметку времени для вызывающего процесса, которая должна содержаться в отправляемой дейтаграмме в виде 32-разрядного целого числа без знака. Мы получаем текущее время и дату из функции gettimeofday и затем вычитаем число секунд в момент вызова функции rtt_init (значение, хранящееся в элементе rtt_base структуры rtt_info). Мы преобразуем это значение в миллисекунды, а также преобразуем в миллисекунды значение, возвращаемое функцией gettimeofday в микросекундах. Тогда отметка времени является суммой этих двух значений в миллисекундах.

Разница во времени между двумя вызовами функции rtt_ts представляется количеством миллисекунд между этими двумя вызовами. Но мы храним отметки времени в 32-разрядном целом числе без знака, а не в структуре timeval.

43-47 Функция rtt_newpack просто обнуляет счетчик повторных передач. Эта функция должна вызываться всегда, когда новый пакет отправляется в первый раз.

48-53 Функция rtt_start возвращает текущее значение RTO в миллисекундах. Возвращаемое значение затем может использоваться в качестве аргумента функции alarm.

Функция rtt_stop, показанная в листинге 22.11, вызывается после получения ответа для обновления оценочного значения RTT и вычисления нового значения RTO.

Листинг 22.11. Функция rtt_stop: обновление показателей RTT и вычисление нового

//lib/rtt.c

62 void

63 rtt_stop(struct rtt_info *ptr, uint32_t ms)

64 {

65  double delta;

66  ptr->rtt_rtt = ms / 1000.0; /* измеренное значение RTT в секундах */

67  /*

68   * Обновляем оценочные значения RTT среднего отклонения RTT.

69   * (См. статью Джекобсона (Jacobson). SIGCOMM'88. Приложение А.)

70   * Здесь мы для простоты используем числа с плавающей точкой.

71   */

72  delta = ptr->rtt_rtt - ptr->rtt_srtt;

73  ptr->rtt_srtt += delta / 8; /* g - 1/8 */

74  if (delta < 0.0)

75   delta = -delta; /* |delta| */

76  ptr->rtt_rttvar += (delta - ptr->rtt_rttvar) / 4; /* h - 1/4 */

77  ptr->rtt_rto = rtt_minmax(RTT_RTOCALC(ptr));

78 }

62-78 Вторым аргументом является измеренное RTT, полученное вызывающим процессом при вычитании полученной в ответе отметки времени из текущей (функция rtt_ts). Затем применяются уравнения, приведенные в начале этого раздела, и записываются новые значения переменных rtt_srtt, rtt_rttvar и rtt_rto.

Последняя функция, rtt_timeout показана в листинге 22.12. Эта функция вызывается, когда истекает время таймера повторных передач.

Листинг 22.12. Функция rtt_timeout: применение экспоненциального смещения

//lib/rtt.c

83 int

84 rtt_timeout(struct rtt_info *ptr)

85 {

86  ptr->rtt_rto *= 2; /* следующее значение RTO */

87  if (++ptr->rtt_nrexmt > RTT_MAXNREXMT)

88   return (-1); /* закончилось время, отпущенное на попытки отправить

                     этот пакет */

89  return (0);

90 }

86 Текущее значение RTO удваивается — в этом и заключается экспоненциальное смещение.

87-89 Если мы достигли максимально возможного количества повторных передач, возвращается значение -1, указывающее вызывающему процессу, что дальнейшие попытки передачи должны прекратиться. В противном случае возвращается 0.

В нашем примере клиент соединялся дважды с двумя различными эхо-серверами в Интернете утром рабочего дня. Каждому серверу было отправлено по 500 строк. По пути к первому серверу было потеряно 8 пакетов, по пути ко второму — 16. Один из потерянных шестнадцати пакетов, предназначенных второму серверу, был потерян дважды, то есть пакет пришлось дважды передавать повторно, прежде чем был получен ответ. Все остальные потерянные пакеты пришлось передать повторно только один раз. Мы могли убедиться, что эти пакеты были действительно потеряны, посмотрев на выведенные порядковые номера каждого из полученных пакетов. Если пакет лишь опоздал, но не был потерян, после повторной передачи клиент получает два ответа: соответствующий запоздавшему первому пакету и повторно переданному. Обратите внимание, что у нас нет возможности определить, что именно было потеряно (и привело к необходимости повторной передачи клиентского запроса) — сам клиентский запрос или же ответ сервера, высланный после получения такого запроса.

ПРИМЕЧАНИЕ

Для первого издания этой книги автор написал для проверки этого клиента сервер UDP, который случайным образом игнорировал пакеты. Теперь он не используется. Нужно только соединить клиент с сервером через Интернет, и тогда нам почти гарантирована потеря некоторых пакетов!

22.6. Связывание с адресами интерфейсов

Одно из типичных применений функции get_ifi_info связано с приложениями UDP, которым нужно выполнять мониторинг всех интерфейсов на узле, чтобы знать, когда и на какой интерфейс приходит дейтаграмма. Это позволяет получающей программе узнавать адрес получателя дейтаграммы UDP, так как именно по этому адресу определяется сокет, на который доставляется дейтаграмма, даже если узел не поддерживает параметр сокета IP_RECVDSTADDR.

ПРИМЕЧАНИЕ

Вспомните наше обсуждение в конце раздела 22.2. Если узел использует более распространенную модель системы с гибкой привязкой (см. раздел 8.8), IP-адрес получателя может отличаться от IP-адреса принимающего интерфейса. В этом случае мы можем определить только адрес получателя дейтаграммы, который не обязательно должен быть адресом, присвоенным принимающему интерфейсу. Чтобы определить принимающий интерфейс, требуется параметр сокета IP_RECVIF или IPV6_PKTINFO.

В листинге 22.13 показана первая часть примера применения этой технологии к эхо-серверу UDP, который связывается со всеми адресами направленной передачи, широковещательной передачи и, наконец, с универсальными адресами.

Листинг 22.13. Первая часть сервера UDP, который с помощью функции bind связывается со всеми адресами

//advio/udpserv03.c

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