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

ЖАНРЫ

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

Таблица 28.1. Значения переменной icmpd_errno, сопоставляющей ошибки ICMPv4 и ICMPv6

icmpd_errno Ошибка ICMPv4 Ошибка ICMPv6
ECONNREFUSED Port unreachable (Порт недоступен) Port unreachable (Порт недоступен)
EMSGSIZE Fragmentation needed but DF bit set (Необходима фрагментация, но установлен бит DF) Packet too big (Слишком большой пакет)
EHOSTUNREACH Time exceeded (Превышено время
передачи)
Time exceeded (Превышено время передачи)
EHOSTUNREACH Source quench (Отключение отправителя)
EHOSTUNREACH Все другие сообщения о недоступности получателя (Destination unreachable) Все другие сообщения о недоступности получателя (Destination unreachable)

Демон возвращает пять типов ошибок ICMP:

1. «Port unreachable» (Порт недоступен) означает, что сокет не связан с портом получателя на IP-адресе получателя.

2. «Packet too big» (Слишком большой пакет) используется при определении транспортной MTU. В настоящее время нет определенного API, позволяющего UDP-приложениям осуществлять определение транспортной MTU. Если ядро поддерживает определение транспортной MTU для UDP, то обычно получение данной ошибки ICMP заставляет ядро записать новое значение транспортной MTU в таблицу маршрутизации ядра, но UDP-приложение, пославшее дейтаграмму, не извещается. Вместо этого приложение должно дождаться истечения тайм-аута и повторно послать дейтаграмму, и тогда ядро найдет новое (меньшее) значение MTU в своей таблице маршрутизации и фрагментирует дейтаграмму. Передача этой ошибки приложению позволяет ему ускорить повторную передачу дейтаграммы, и возможно, приложение сможет уменьшить размер посылаемой дейтаграммы.

3. Ошибка «Time exceeded» (Превышено время передачи) обычно возникает с кодом 0 и означает, что либо значение поля TTL (в случае IPv4), либо предельное количество транзитных узлов (в случае IPv6) достигло нуля. Обычно это свидетельствует о зацикливании маршрута, что, возможно, является временной ошибкой.

4. Ошибка «Source quench» (Отключение отправителя) ICMPv4 хотя и рассматривается в RFC 1812 [6] как устаревшая, может быть послана маршрутизаторами (или неправильно сконфигурированными узлами, действующими как маршрутизаторы). Такие ошибки означают, что пакет отброшен, и поэтому обрабатываются как ошибки недоступности получателя. Следует отметить, что в версии IPv6 нет ошибки отключения отправителя.

5. Все остальные ошибки недоступности получателя (Destination unreachble) означают, что пакет сброшен.

10
Элемент
icmpd_dest
является структурой адреса сокета, содержащей IP-адрес получателя и порта дейтаграммы, сгенерировавшей ICMP-ошибку. Этот элемент может быть структурой
sockaddr_in
для ICMPv4 либо структурой
sockaddr_in6
для ICMPv6. Если приложение посылает дейтаграммы по нескольким адресам, оно, вероятно, имеет по одной структуре адреса сокета на каждый адрес. Возвращая эту информацию в структуре адреса сокета, приложение может сравнить ее со своими собственными структурами для поиска той, которая вызвала ошибку. Тип
sockaddr_storage
используется для того, чтобы в структуре можно было хранить адреса любого типа, поддерживаемого системой.

Эхо-клиент UDP, использующий демон icmpd

Теперь модифицируем наш эхо-клиент UDP (функцию

dg_cli
) для использования нашего демона
icmpd
. В листинге 28.21 приведена первая половина функции.

Листинг 28.21. Первая часть приложения dg_cli

//icmpd/dgcli01.c

1 #include "unpicmpd.h"

2 void

3 dg_cli(FILE *fp, int sockfd, const SA *pservadd, socklen_t servlen)

4 {

5 int icmpfd, maxfdp1;

6 char sendline[MAXLINE], recvline[MAXLINE + 1];

7 fd_set rset;

8 ssize_t n;

9 struct timeval tv;

10 struct icmpd_err icmpd_err;

11 struct sockaddr_un sun;

12 Sock_bind_wild(sockfd, pservaddr->sa_family);

13 icmpfd = Socket(AF_LOCAL, SOCK_STREAM, 0);

14 sun.sun_family = AF_LOCAL;

15 strcpy(sun.sun_path, ICMPD_PATH);

16 Connect(icmpfd, (SA*)&sun, sizeof(sun));

17 Write_fd(icmpfd, "1", 1, sockfd);

18 n = Read(icmpfd, recvline, 1);

19 if (n != 1 || recvline[0] != '1')

20 err_quit("error creating icmp socket, n = %d, char = %c",

21 n, recvline[0]);

22 FD_ZERO(&rset);

23 maxfdp1 = max(sockfd, icmpfd) + 1;

2-3
Аргументы функции те же, что и во всех ее предыдущих версиях.

Связывание с универсальным адресом и динамически назначаемым портом

12
Вызываем функцию
sock_bind_wild
для связывания при помощи функции
bind
универсального IP-адреса и динамически назначаемого порта с UDP-сокетом. Таким образом копия сокета, который пересылается демону, оказывается связана с портом, поскольку демону необходимо знать этот порт.

ПРИМЕЧАНИЕ

Демон также может произвести подобное связывание, если локальный порт не был связан с сокетом, который был передан демону, но это работает не во всех системах. В реализациях SVR4, таких как Solaris 2.5, сокеты не являются частью ядра, и когда один процесс связывает (bind) порт с совместно используемым сокетом, другой процесс при попытке использовать копию этого сокета получает ошибки. Простейшее решение — потребовать, чтобы приложение связывало локальный порт прежде, чем передавать сокет демону.

Установление доменного соединения Unix с демоном

13-16
Мы создаем сокет семейства
AF_INET
и подключаемся к известному имени сервера при помощи вызова
connect
.

Отправка UDP-сокета демону, ожидание ответа от демона

17-21
Вызываем функцию
write_fd
, приведенную в листинге 15.11 для отправки копии UDP-сокета демону. Мы также посылаем одиночный байт данных — символ
"1"
, поскольку некоторые реализации не передают дескриптор без данных. Демон посылает обратно одиночный байт данных, состоящий из символа
"1"
, для обозначения успешного выполнения. Любой другой ответ означает ошибку.

22-23
Инициализируем набор дескрипторов и вычисляем первый аргумент для функции
select
(максимальный из двух дескрипторов, увеличенный на единицу).

Вторая половина нашего клиента приведена в листинге 28.22. Это цикл, который считывает данные из стандартного ввода, посылает строку серверу, считывает ответ сервера и записывает ответ в стандартный вывод.

Листинг 28.22. Вторая часть приложения dg_cli

//icmpd/dgcli01.c

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