TCP — это потоковый протокол, использующий
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
).
Чтобы можно было получить два последних элемента, мы определяем в нашем заголовке unp.h
следующую структуру:
struct in_pktinfo {
struct in_addr ipi_addr; /* IPv4-адрес получателя */
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-адреса.