freebsd % udpcli01 aix-6 echo hello, world
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 "unpicmpd.h"
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;
11 struct sockaddr_un cliaddr;
12 /* прототипы функций */
13 int readable_conn(int);
14 int readable_listen(void);
15 int readable_v4(void);
16 int readable_v6(void);
2-17
Поскольку демон может обрабатывать любое количество клиентов, для сохранения информации о каждом клиенте используется массив структур client
. Они аналогичны структурам данных, которые использовались в разделе 6.8. Кроме дескриптора для доменного сокета Unix, через который осуществляется связь с клиентом, сохраняется также семейство адресов клиентского UDP-сокета AF_INET
или AF_INET6
и номер порта, связанного с сокетом. Далее объявляются прототипы функций и глобальные переменные, совместно используемые этими функциями.
В листинге 28.24 приведена первая часть функции main.
Листинг 28.24. Первая часть функции main: создание сокетов
//icmpd/icmpd.c
1 #include "icmpd.h"
2 int
3 main(int argc, char **argv)
4 {
5 int i, sockfd;
6 struct sockaddr_un sun;
7 if (argc != 1)
8 err_quit("usage: icmpd");
9 maxi = -1; /* индекс массива client[] */
10 for (i = 0; i < FD_SETSIZE; i++)
11 client[i].connfd = -1; /* -1 означает свободный элемент */
12 FD_ZERO(&allset);
13 fd4 = Socket(AF_INET, SOCK_RAW, IPPROTO_ICMP);
14 FD_SET(fd4, &allset);
15 maxfd = fd4;
16 #ifdef IPV6
17 fd6 = Socket(AF_INET6, SOCK_RAW, IPPROTO_ICMPV6);
18 FD_SET(fd6, &allset);
19 maxfd = max(maxfd, fd6);
20 #endif
21 listenfd = Socket(AF_UNIX, SOCK_STREAM, 0);
22 sun.sun_family = AF_LOCAL;
23 strcpy(sun.sun_path, ICMPD_PATH);
24 unlink(ICMPD_PATH);
25 Bind(listenfd, (SA*)&sun, sizeof(sun));
26 Listen(listenfd, LISTENQ);
27 FD_SET(listenfd, &allset);
28 maxfd = max(maxfd, listenfd);
9-10
Инициализируется массив client
путем присваивания значения -1 элементу присоединенного сокета.
12-28
Создаются три сокета: символьный сокет ICMPv4, символьный сокет ICMPv6 и потоковый доменный сокет Unix. Мы связываем при помощи функции bind
свое заранее известное полное имя с сокетом и вызываем функцию listen
. Это сокет, к которому клиенты присоединяются с помощью функции connect
. Для функции select
также вычисляется максимальный дескриптор, а для вызовов функции accept
в памяти размещается структура адреса сокета.
В листинге 28.25 приведена вторая часть функции main
. Она содержит бесконечный цикл, вызывающий функцию select
в ожидании, когда будет готов к чтению какой-либо из дескрипторов демона.
Листинг 28.25. Вторая часть функции main: обработка готового к чтению дескриптора
//icmpd/icmpd.c
29 for (;;) {
30 rset = allset;
31 nready = Select(maxfd+1, &rset, NULL, NULL, NULL);