Чтение онлайн

ЖАНРЫ

UNIX: разработка сетевых приложений
Шрифт:

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-адреса.

ПРИМЕЧАНИЕ

К сожалению, Беркли-ядра принимают дейтаграммы, предназначенные для адреса 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)

6 {

7 struct msghdr msg;

8 struct iovec iov[1];

9 ssize_t n;

10 #ifdef HAVE_MSGHDR_MSG_CONTROL

11 struct cmsghdr *cmptr;

12 union {

13 struct cmsghdr cm;

14 char control[CMSG_SPACE(sizeof(struct in_addr)) +

15 CMSG_SPACE(sizeof(struct unp_in_pktinfo))];

16 } control_un;

17 msg.msg_control = control_un.control;

18 msg.msg_controllen = sizeof(control_un.control);

19 msg.msg_flags = 0;

20 #else

21 bzero(&msg, sizeof(msg)); /* обнуление msg_accrightslen = 0 */

22 #endif

23 msg.msg_name = sa;

24 msg.msg_namelen = *salenptr;

Поделиться с друзьями: