Литмир - Электронная Библиотека
Содержание  
A
A

25   Sendto(sockfd, sendline, strlen(sendline), 0, pservaddr, servlen);

26   tv.tv_sec = 5;

27   tv.tv_usec = 0;

28   FD_SET(sockfd, &rset);

29   FD_SET(icmpfd, &rset);

30   if ((n = Select(maxfdp1, &rset, NULL, NULL, &tv)) == 0) {

31    fprintf(stderr, "socket timeout\n");

32    continue;

33   }

34   if (FD_ISSET(sockfd, &rset)) {

35    n = Recvfrom(sockfd, recvline, MAXLINE, 0, NULL, NULL);

36    recvline[n] = 0; /* завершающий нуль */

37    Fputs(recvline, stdout);

38   }

39   if (FD_ISSET(icmpfd, &rset)) {

40    if ((n = Read(icmpfd, &icmpd_err, sizeof(icmpd_err))) == 0)

41     err_quit("ICMP daemon terminated");

42    else if (n != sizeof(icmpd_err))

43     err_quit("n = %d, expected %d", n, sizeof(icmpd_err)),

44    printf("ICMP error: dest = %s, %s, type = %d, code = %d\n",

45     Sock_ntop(&icmpd_err.icmpd_dest, icmpd_err.icmpd_len);

46    strerror(icmpd_err.icmpd_errno),

47     icmpd_err.icmpd_type, icmpd_err.icmpd_code);

48   }

49  }

50 }

Вызов функции select

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

Вывод ответа сервера

34-38
 Если дейтаграмма возвращается сервером, она выводится в стандартный поток вывода.

Обработка ICMP-ошибки

39-48
 Если наше доменное соединение Unix с демоном
icmpd
готово для чтения, мы пытаемся прочитать структуру
icmpd_err
. Если это удается, выводится соответствующая информация, возвращаемая демоном.

ПРИМЕЧАНИЕ

Функция strerror является примером простой, почти тривиальной функции, которая должна быть более переносимой, чем она есть. В ANSI С ничего не говорится об ошибках, возвращаемых этой функцией. В руководстве по операционной системе Solaris 2.5 говорится, что функция возвращает пустой указатель, если ее аргумент выходит за пределы допустимых значений. Это означает, что код наподобие следующего:

printf("%s", strerror(arg));

является некорректным, поскольку strerror может вернуть пустой указатель. Однако реализации FreeBSD, так же как и все реализации исходного кода, которые автор смог найти, обрабатывают неправильный аргумент, возвращая указатель на строку типа «Неизвестная ошибка». Это имеет смысл и означает, что приведенный выше код правильный. POSIX изменил ситуацию, утверждая, что поскольку не предусмотрено значение, сигнализирующее об ошибке, связанной с выходом аргумента за допустимые пределы, функция присваивает переменной errno значение EIVAL. (Ничего не сказано об указателе, возвращаемом в случае ошибки.) Это означает, что полностью правильный код должен обнулить errno, вызвать функцию strerror, проверить, не равняется ли значение errno величине EINVAL, и в случае ошибки вывести некоторое сообщение.

Примеры эхо-клиента UDP

Приведем несколько примеров работы данного клиента, прежде чем рассматривать исходный код демона. Сначала посылаем дейтаграмму на IP-адрес, не связанный с Интернетом:

freebsd % <b>udpcli01 192.0.2.5 echo</b>

<b>hi there</b>

socket timeout

<b>and hello</b>

socket timeout

Мы считаем, что демон

icmpd
запущен, и ждем возвращения каким-либо маршрутизатором ICMP-ошибок недоступности получателя. Вместо этого наше приложение завершается по превышению времени ожидания. Мы показываем это, чтобы повторить, что время ожидания все еще необходимо, а генерация ICMP- сообщения о недоступности узла может и не произойти.

В следующем примере дейтаграмма отправляется на порт стандартного эхо- сервера узла, на котором этот сервер не запущен. Мы получаем ожидаемое ICMPv4-сообщение о недоступности порта.

freebsd % <b>udpcli01 aix-4 echo</b>

<b>hello</b>

ICMP error: dest = 192.168.42.2:7. Connection refused, type = 3, code = 1

Выполнив ту же попытку с протоколом IPv6, мы получаем ICMPv6-сообщение о недоступности порта.

freebsd % <b>udpcli01 aix-6 echo hello, world</b>

ICMP error: dest = [3ffe:b80:1f8d:2:204:acff:fe17:bf38]:7. Connection refused, type = 1. code = 4

Демон icmpd

Начинаем описание нашего демона

icmpd
с заголовочного файла
icmpd.h
, приведенного в листинге 28.23.

Листинг 28.23. Заголовочный файл icmpd.h для демона icmpd

//icmpd/icmpd.h

 1 #include &quot;unpicmpd.h&quot;

 2 struct client {

 3  int connfd; /* потоковый доменный сокет Unix к клиенту */

 4  int family; /* AF_INET или AF_INET6 */

 5  int lport;  /* локальный порт, связанный с UDP-сокетом клиента */

 6              /* сетевой порядок байтов */

 7 } client[FD_SETSIZE];

 8 /* глобальные переменные */

 9 int fd4, fd6, listenfd, maxi, maxfd, nready;

10 fd_set rset, allset;

324
{"b":"225366","o":1}