UDP — это ненадежный протокол, однако существуют приложения, в которых UDP использовать целесообразнее, чем TCP. Мы рассмотрим факторы, под влиянием которых UDP оказывается предпочтительнее TCP. В UDP-приложения необходимо включать ряд функций, в некоторой степени компенсирующих ненадежность UDP: тайм-аут и повторную передачу, обработку потерянных дейтаграмм и порядковые номера для сопоставления ответов запросам. Мы разработаем набор функций, которые сможем вызывать из наших приложений UDP.
Если реализация не поддерживает параметр сокета
IP_RECVDSTADDR
, один из способов определить IP-адрес получателя UDP-дейтаграммы заключается в связывании
всех интерфейсных адресов и использовании функции
select
.
Большинство серверов UDP являются последовательными, но существуют приложения, обменивающиеся множеством дейтаграмм UDP между клиентом и сервером, что требует параллельной обработки. Примером может служить TFTP (Trivial File Transfer Protocol — упрощенный протокол передачи файлов). Мы рассмотрим два варианта подобного согласования — с использованием суперсервера
inetd
и без него.
В завершение этой главы мы рассмотрим информацию о пакете, которая может быть передана во вспомогательных данных дейтаграммы IPv6: IP-адрес отправителя, отправляющий интерфейс, предельное количество транзитных узлов исходящих дейтаграмм и адрес следующего транзитного узла. Аналогичная информация — IP-адрес получателя, принимающий интерфейс и предельное количество транзитных узлов — может быть получена вместе с дейтаграммой IPv6.
22.2. Получение флагов, IP-адреса получателя и индекса интерфейса
Исторически функции
sendmsg
и
recvmsg
использовались только для передачи дескрипторов через доменные сокеты Unix (см. раздел 15.7), но даже это происходило сравнительно редко. Однако в настоящее время популярность этих двух функций растет по двум причинам:
1. Элемент
msg_flags
, добавленный в структуру
msghdr
в реализации 4.3BSD Reno, возвращает приложению флаги сообщения. Эти флаги мы перечислили в табл. 14.2.
2. Вспомогательные данные используются для передачи все большего количества информации между приложением и ядром. В главе 27 мы увидим, что IPv6 продолжает эту тенденцию.
В качестве примера использования функции
recvmsg
мы напишем функцию
recvfrom_flags
, аналогичную функции recvfrom, но дополнительно позволяющую получить:
возвращаемое значение
msg_flags
;
адрес получателя полученной дейтаграммы (из параметра сокета
IP_RECVDSTADDR
);
индекс интерфейса, на котором была получена дейтаграмма (параметр сокета
IP_RECVIF
).
Чтобы можно было получить два последних элемента, мы определяем в нашем заголовке
int ipi_ifindex; /* индекс интерфейса, на котором была
получена дейтаграмма */
};
Мы выбрали имена структуры и ее элементов так, чтобы получить определенное сходство со структурой IPv6
in6_pktinfo
, возвращающей те же два элемента для сокета IPv6 (см. раздел 22.8). Наша функция
recvfrom_flags
будет получать в качестве аргумента указатель на структуру
in_pktinfo
, и если этот указатель не нулевой, возвращать структуру
через указатель.
Проблема построения этой структуры состоит в том, что неясно, что возвращать, если недоступна информация, которая должна быть получена из параметра сокета
IP_RECVDSTADDR
(то есть реализация не поддерживает данный параметр сокета). Обработать индекс интерфейса легко, поскольку нулевое значение может использоваться как указание на то, что индекс неизвестен. Но для IP-адреса все 32-разрядные значения являются действительными. Мы выбрали такое решение: адрес получателя 0.0.0.0 возвращается в том случае, когда действительное значение недоступно. Хотя это реальный IP-адрес, использовать его в качестве IP-адреса получателя не разрешается (RFC 1122 [10]). Он будет действителен только в качестве IP-адреса отправителя во время начальной загрузки узла, когда узел еще не знает своего IP-адреса.
ПРИМЕЧАНИЕ
К сожалению, Беркли-ядра принимают дейтаграммы, предназначенные для адреса 0.0.0.0 [128, с. 218-219]. Это устаревшие адреса широковещательной передачи, генерируемые ядрами 4.2BSD.
Первая часть нашей функции
recvfrom_flags
представлена в листинге 22.1 [1] . Эта функция предназначена для использования с сокетом UDP.
Листинг 22.1. Функция recvfrom_flags: вызов функции recvmsg
1
Все исходные коды программ, опубликованные в этой книге, вы можете найти по адресу http://www.piter.com.
//adviо/recvfromflags.c
1 #include "unp.h"
2 #include <sys/param.h> /* макрос ALIGN для макроса CMSG_NXTHDR */
3 ssize_t
4 recvfrom_flags(int fd, void *ptr, size_t nbytes, int *flagsp,
5 SA *sa, socklen_t *salenptr, struct unp_in_pktinfo *pktp)