Уильям Стивенс - UNIX: разработка сетевых приложений
- Название:UNIX: разработка сетевых приложений
- Автор:
- Жанр:
- Издательство:Питер
- Год:2007
- Город:Санкт-Петербург
- ISBN:5-94723-991-4
- Рейтинг:
- Избранное:Добавить в избранное
-
Отзывы:
-
Ваша оценка:
Уильям Стивенс - UNIX: разработка сетевых приложений краткое содержание
Новое издание книги, посвященной созданию веб-серверов, клиент-серверных приложений или любого другого сетевого программного обеспечения в операционной системе UNIX, — классическое руководство по сетевым программным интерфейсам, в частности сокетам. Оно основано на трудах Уильяма Стивенса и полностью переработано и обновлено двумя ведущими экспертами по сетевому программированию. В книгу включено описание ключевых современных стандартов, реализаций и методов, она содержит большое количество иллюстрирующих примеров и может использоваться как учебник по программированию в сетях, так и в качестве справочника для опытных программистов.
UNIX: разработка сетевых приложений - читать онлайн бесплатно ознакомительный отрывок
Интервал:
Закладка:
Данная программа состоит из двух частей: одна половина читает все, что приходит на символьный сокет, и выводит эхо-ответы ICMP, а другая половина один раз в секунду посылает эхо-запросы ICMP. Вторая половина запускается один раз в секунду сигналом SIGALRM
.
В листинге 28.2 приведен заголовочный файл ping.h
, подключаемый во всех файлах программы.
Листинг 28.2. Заголовочный файл ping.h
//ping/ping.h
1 #include "unp.h"
2 #include
3 #include
4 #include
5 #define BUFSIZE 1500
6 /* глобальные переменные */
7 char sendbuf[BUFSIZE];
8 int datalen; /* размер данных после заголовка ICMP */
9 char *host;
10 int nsent; /* увеличиваем на 1 для каждого sendto() */
11 pid_t pid; /* наш PID */
12 int sockfd;
13 int verbose;
14 /* прототипы функций */
15 void init_v6(void);
16 void proc_v4(char*, ssize_t, struct msghdr*, struct timeval*);
17 void proc_v6(char*, ssize_t., struct msghdr*, struct timeval*);
18 void send_v4(void);
19 void send_v6(void):
20 void readloop(void);
21 void sig_alrm(int);
22 void tv_sub(struct timeval*, struct timeval*);
23 struct proto {
24 void (*fproc)(char*, ssize_t, struct msghdr*, struct timeval*);
25 void (*fsend)(void);
26 void (*finit)(void);
27 struct sockaddr *sasend; /* структура sockaddr{} для отправки,
полученная от getaddrinfo */
28 struct sockaddr *sarecv; /* sockaddr{} для получения */
29 socklen_t salen; /* длина sockaddr{} */
30 int icmpproto; /* значение IPPROTO_xxx для ICMP */
31 } *pr;
32 #ifdef IPV6
33 #include
34 #include
35 #endif
1-22
Подключаются основные заголовочные файлы IPv4 и ICMPv4, определяются некоторые глобальные переменные и прототипы функций.
23-31
Для обработки различий между IPv4 и IPv6 используется структура proto
. Данная структура содержит два указателя на функции, два указателя на структуры адреса сокета, размер структуры адреса сокета и значение протокола для ICMP. Глобальный указатель pr
будет указывать на одну из этих структур, которая будет инициализироваться для IPv4 или IPv6.
32-35
Подключаются два заголовочных файла, определяющие структуры и константы IPv6 и ICMPv6 (RFC 3542 [114]).
Функция main
приведена в листинге 28.3.
Листинг 28.3. Функция main
//ping/main.c
1 #include "ping.h"
2 struct proto proto_v4 =
3 { proc_v4, send_v4, NULL, NULL, NULL, 0, IPPROTO_ICMP };
4 #ifdef IPV6
5 struct proto proto_v6 =
6 { proc_v6, send_v6, init_v6, NULL, NULL, 0, IPPROTO_ICMPV6 };
7 #endif
8 int datalen = 56; /* размер данных в эхо-запросе ICMP */
9 int
10 main(int argc, char **argv)
11 {
12 int c;
13 struct addrinfo *ai;
14 char *h;
15 opterr = 0; /* отключаем запись сообщений getopt() в stderr */
16 while ((с = getopt(argc, argv, "v")) != -1) {
17 switch (c) {
18 case 'v':
19 verbose++;
20 break;
21 case '?':
22 err_quit("unrecognized option %c", c);
23 }
24 }
25 if (optind != argc-1)
26 err_quit("usage: ping [ -v ] ");
27 host = argv[optind];
28 pid = getpid() & 0xffff; /* поле идентификатора ICMP имеет размер 16 бит */
29 Signal(SIGALRM, sig_alrm);
30 ai = Host_serv(host, NULL, 0, 0);
31 h = Sock_ntop_host(ai->ai_addr, ai->ai_addrlen);
32 printf("PING %s (%s): %d data bytes\n",
33 ai->ai_canonname ? ai->ai_canonname : h, h, datalen);
34 /* инициализация в соответствии с протоколом */
35 if (ai->ai_family == AF_INET) {
36 pr = &proto_v4;
37 #ifdef IPV6
38 } else if (ai->ai_family == AF_INET6) {
39 pr = &proto_v6;
40 if (IN6_IS_ADDR_V4MAPPED(&(((struct sockaddr_in6*)
41 ai->ai_addr)->sin6_addr)))
42 err_quit("cannot ping IPv4-mapped IPv6 address");
43 #endif
44 } else
45 err_quit("unknown address family %d", ai->ai_family);
46 pr->sasend = ai->ai_addr;
47 pr->sarecv = Calloc(1, ai->ai_addrlen);
48 pr->salen = ai->ai_addrlen;
49 readloop();
50 exit(0);
51 }
2-7
Определяется структура proto
для IPv4 и IPv6. Указатели структуры адреса сокета инициализируются как нулевые, поскольку еще не известно, какая из версий будет использоваться — IPv4 или IPv6.
8
Устанавливается количество дополнительных данных (56 байт), которые будут посылаться с эхо-запросом ICMP. При этом полная IPv4-дейтаграмма будет иметь размер 84 байта (20 байт на IPv4-заголовок и 8 байт на ICMP-заголовок), а IPv6-дейтаграмма будет иметь длину 104 байта. Все данные, посылаемые с эхо- запросом, должны быть возвращены в эхо-ответе. Время отправки эхо-запроса будет сохраняться в первых 8 байтах области данных, а затем, при получении эхо- ответа, будет использоваться для вычисления и вывода времени RTT.
15-24
Единственный параметр командной строки, поддерживаемый в нашей версии, это параметр -v
, в результате использования которого большинство ICMP-сообщений будут выводиться на консоль. (Мы не выводим эхо-ответы, принадлежащие другой запущенной копии программы ping
.) Для сигнала SIGALRM
устанавливается обработчик, и мы увидим, что этот сигнал генерируется один раз в секунду и вызывает отправку эхо-запросов ICMP.
31-48
Строка, содержащая имя узла или IP-адрес, является обязательным аргументом и обрабатывается функцией host_serv
. Возвращаемая структура addrinfo
содержит семейство протоколов — либо AF_INET
, либо AF_INET6
. Глобальный указатель pr устанавливается на требуемую в конкретной ситуации структуру proto
. Также с помощью вызова функции IN6_IS_ADDR_V4MAPPED
мы убеждаемся, что адрес IPv6 на самом деле не является адресом IPv4, преобразованным к виду IPv6, поскольку даже если возвращаемый адрес является адресом IPv6, узлу будет отправлен пакет IPv4. (Если такая ситуация возникнет, можно переключиться и использовать IPv4.) Структура адреса сокета, уже размещенная в памяти с помощью функции getaddrinfo
, используется для отправки, а другая структура адреса сокета того же размера размещается в памяти для получения.
Обработка ответов осуществляется функцией readlоор
, представленной в листинге 28.4.
Листинг 28.4. Функция readloop
//ping/readlоор.c
1 #include "ping.h"
2 void
3 readloop(void)
4 {
5 int size;
6 char recvbuf[BUFSIZE];
7 char controlbuf[BUFSIZE];
8 struct msghdr msg;
9 struct iovec iov;
10 ssize_t n;
11 struct timeval tval;
12 sockfd = Socket(pr->sasend->sa_family, SOCK_RAW, pr->icmpproto);
13 setuid(getuid()); /* права привилегированного пользователя
больше не нужны */
14 if (pr->finit)
15 (*pr->finit)();
16 size = 60 * 1024; /* setsockopt может завершиться с ошибкой */
17 setsockopt(sockfd, SOL_SOCKET, SO_RCVBUF, &size, sizeof(size));
Интервал:
Закладка: