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

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

На нашем сайте KnigaRead.com Вы можете абсолютно бесплатно читать книгу онлайн Уильям Стивенс, "UNIX: разработка сетевых приложений" бесплатно, без регистрации.
Перейти на страницу:

 4 void

 5 dg_echo(int sockfd, SA *pcliaddr, socklen_t clilen)

 6 {

 7  int flags;

 8  const int on = 1;

 9  socklen_t len;

10  ssize_t n;

11  char mesg[MAXLINE], str[INET6_ADDRSTRLEN], ifname[IFNAMSIZ];

12  struct in_addr in_zero;

13  struct in_pktinfo pktinfo;


14 #ifdef IP_RECVDSTADDR

15  if (setsockopt(sockfd, IPPROTO_IP, IP_RECVDSTADDR, &on, sizeof(on)) < 0)

16   err_ret("setsockopt of IP_RECVDSTADDR");

17 #endif

18 #ifdef IP_RECVIF

19  if (setsockopt(sockfd, IPPROTO_IP, IP_RECVIF, &on, sizeof(on)) < 0)

20   err_ret("setsockopt of IP_RECVIF");

21 #endif

22  bzero(&in_zero, sizeof(struct in_addr)); /* IPv4-адрес, состоящий

                                                из одних нулей */


23  for (;;) {

24   len = clilen;

25   flags = 0;

26   n = Recvfrom_flags(sockfd, mesg, MAXLINE, &flags,

27    pcliaddr, &len, &pktinfo);

28   printf("%d-byte datagram from %s", n, Sock_ntop(pcliaddr, len));

29   if (memcmp(&pktinfo.ipi_addr, &in_zero, sizeof(in_zero)) != 0)

30    printf(", to %s", Inet_ntop(AF_INET, &pktinfo.ipi_addr,

31     str, sizeof(str)));

32   if (pktinfo.ipi_ifindex > 0)

33    printf(", recv i/f = %s",

34    If_indextoname(pktinfо.ipi_ifindex, ifname));

35 #ifdef MSG_TRUNC

36   if (flags & MSG_TRUNC)

37    printf(" (datagram truncated)");

38 #endif

39 #ifdef MSG_CTRUNC

40   if (flags & MSG_CTRUNC)

41    printf(" (control info truncated)");

42 #endif

43 #ifdef MSG_BCAST

44   if (flags & MSG_BCAST)

45    printf(" (broadcast)");

46 #endif

47 #ifdef MSG_MCAST

48   if (flags & MSG_MCAST)

49    printf(" (multicast)");

50 #endif

51   printf("n");


52   Sendto(sockfd, mesg, n, 0, pcliaddr, len);

53  }

54 }

Изменение MAXLINE

2-3 Мы удаляем существующее определение MAXLINE, имеющееся в нашем заголовочном файле unp.h, и задаем новое значение — 20. Это позволит нам увидеть, что произойдет, когда мы получим дейтаграмму UDP, превосходящую размер буфера, переданного функции (в данном случае функции recvmsg).

Установка параметров сокета IP_RECVDSTADDR и IP_RECVIF

14-21 Если параметр сокета IP_RECVDSTADDR определен, мы включаем его. Аналогично включается параметр сокета IP_RECVIF.

Чтение дейтаграммы, вывод IP-адреса отправителя и порта

24-28 Дейтаграмма читается с помощью вызова функции recvfrom_flags. IP-адрес отправителя и порт ответа сервера преобразуются в формат представления функцией sock_ntop.

Вывод IP-адреса получателя

29-31 Если возвращаемый IP-адрес ненулевой, он преобразуется в формат представления функцией inet_ntop и выводится.

Вывод имени интерфейса, на котором была получена дейтаграмма

32-34 Если индекс интерфейса ненулевой, его имя будет возвращено функцией if_indextoname. Это имя наша функция печатает на экране.

Проверка различных флагов

35-51 Мы проверяем четыре дополнительных флага и выводим сообщение, если какие-либо из них установлены.

22.3. Обрезанные дейтаграммы

В системах, происходящих от BSD, при получении UDP-дейтаграммы, размер которой больше буфера приложения, функция recvmsg устанавливает флаг MSG_TRUNC в элементе msg_flags структуры msghdr (см. табл. 14.2). Все Беркли-реализации, поддерживающие структуру msghdr с элементом msg_flags, обеспечивают это уведомление.

ПРИМЕЧАНИЕ

Это пример флага, который должен быть возвращен процессу ядром. В разделе 14.3 мы упомянули о проблеме разработки функций recv и recvfrom: их аргумент flags является целым числом, что позволяет передавать флаги от процесса к ядру, но не наоборот.

К сожалению, не все реализации подобным образом обрабатывают ситуацию, когда размер дейтаграммы UDP оказывается больше, чем предполагалось. Возможны три сценария:

1. Лишние байты игнорируются, и приложение получает флаг MSG_TRUNC, что требует вызова функции recvmsg.

2. Игнорирование лишних байтов без уведомления приложения.

3. Сохранение лишних байтов и возвращение их в последующих операциях чтения на сокете.

ПРИМЕЧАНИЕ

POSIX задает первый тип поведения: игнорирование лишних байтов и установку флага MSG_TRUNC. Ранние реализации SVR4 действуют по третьему сценарию.

Поскольку способ обработки дейтаграмм, превышающих размер приемного буфера приложения, зависит от реализации, одним из решений, позволяющий обнаружить ошибку, будет всегда использовать буфер приложения на 1 байт больше самой большой дейтаграммы, которую приложение предположительно может получить. Если все же будет получена дейтаграмма, длина которой равна размеру буфера, это явно будет свидетельствовать об ошибке.

22.4. Когда UDP оказывается предпочтительнее TCP

В разделах 2.3 и 2.4 мы описали основные различия между UDP и TCP. Поскольку мы знаем, что TCP надежен, a UDP — нет, возникает вопрос: когда следует использовать UDP вместо TCP и почему? Сначала перечислим преимущества UDP:

■ Как видно из табл. 20.1, UDP поддерживает широковещательную и направленную передачу. Действительно, использование UDP обязательно, если приложению требуется широковещательная или многоадресная передача. Эти два режима адресации мы рассматривали в главах 20 и 21.

■ UDP не требует установки и разрыва соединения. В соответствии с рис. 2.5 UDP позволяет осуществить обмен запросом и ответом в двух пакетах (если предположить, что размеры запроса и ответа меньше минимального размера MTU между двумя оконечными системами). В случае TCP требуется около 10 пакетов, если считать, что для каждого обмена «запрос-ответ» устанавливается новое соединение TCP.

Для анализа количества передаваемых пакетов важным фактором является также число циклов обращения пакетов, необходимых для получения ответа. Это становится важно, если время ожидания превышает пропускную способность, как показано в приложении А [112]. В этом тексте сказано, что минимальное время транзакции для запроса-ответа UDP равно RTT + SPT, где RTT — это время обращения между клиентом и сервером, a SPT — время обработки запроса сервером. Однако в случае TCP, если для осуществления каждой последовательности «запрос-ответ» используется новое соединение TCP, минимальное время транзакции будет равно 2×RTT+SPT, то есть на один период RTT больше, чем для UDP.

В отношении второго пункта очевидно, что если соединение TCP используется для множества обменов «запрос-ответ», то стоимость установления и разрыва соединения амортизируется во всех запросах и ответах. Обычно это решение предпочтительнее, чем использование нового соединения для каждого обмена «запрос- ответ». Тем не менее существуют приложения, использующие новое соединение для каждого цикла «запрос-ответ» (например, старые версии HTTP). Кроме того, существуют приложения, в которых клиент и сервер обмениваются в одном цикле «запрос-ответ» (например, DNS), а затем могут не обращаться друг к другу в течение часов или дней.

Теперь мы перечислим функции TCP, отсутствующие в UDP. Это означает, что приложение должно само реализовывать эти функции, если они ему необходимы. Мы говорим «необходимы», потому что не все свойства требуются всем приложениям. Например, может не возникнуть необходимости повторно передавать потерянные сегменты для аудиоприложений реального времени, если приемник способен интерполировать недостающие данные. Также для простых транзакций «запрос-ответ» может не потребоваться управление потоком, если два конца соединения заранее договорятся о размерах наибольшего запроса и ответа.

■ Положительные подтверждения, повторная передача потерянных пакетов, обнаружение дубликатов и упорядочивание пакетов, порядок следования которых был изменен сетью. TCP подтверждает получение всех данных, позволяя обнаруживать потерянные пакеты. Реализация этих двух свойств требует, чтобы каждый сегмент данных TCP содержал порядковый номер, по которому можно впоследствии проверить получение данного сегмента. Требуется также, чтобы TCP прогнозировал значение тайм-аута повторной передачи для соединения и чтобы это значение последовательно обновлялось по мере изменения сетевого трафика между конечными точками.

■ Оконное управление потоком. Принимающий TCP сообщает отправляющему, какое буферное пространство он выделил для приема данных, и отправляющий не может превышать этого ограничения. То есть количество неподтвержденных данных отправителя никогда не может стать больше объявленного размера окна принимающего.

■ Медленный старт и предотвращение перегрузки. Это форма управления потоком, осуществляемого отправителем, служащая для определения текущей пропускной способности сети и позволяющая контролировать ситуацию во время переполнения сети. Все современные TCP-приложения должны поддерживать эти два свойства, и опыт (накопленный еще до того, как эти алгоритмы были реализованы в конце 80-х) показывает, что протоколы, не снижающие скорость передачи при перегрузке сети, лишь усугубляют эту перегрузку (см., например, [52]).

Перейти на страницу:
Прокомментировать
Подтвердите что вы не робот:*