Уильям Стивенс - UNIX: разработка сетевых приложений
- Название:UNIX: разработка сетевых приложений
- Автор:
- Жанр:
- Издательство:Питер
- Год:2007
- Город:Санкт-Петербург
- ISBN:5-94723-991-4
- Рейтинг:
- Избранное:Добавить в избранное
-
Отзывы:
-
Ваша оценка:
Уильям Стивенс - UNIX: разработка сетевых приложений краткое содержание
Новое издание книги, посвященной созданию веб-серверов, клиент-серверных приложений или любого другого сетевого программного обеспечения в операционной системе UNIX, — классическое руководство по сетевым программным интерфейсам, в частности сокетам. Оно основано на трудах Уильяма Стивенса и полностью переработано и обновлено двумя ведущими экспертами по сетевому программированию. В книгу включено описание ключевых современных стандартов, реализаций и методов, она содержит большое количество иллюстрирующих примеров и может использоваться как учебник по программированию в сетях, так и в качестве справочника для опытных программистов.
UNIX: разработка сетевых приложений - читать онлайн бесплатно ознакомительный отрывок
Интервал:
Закладка:
18 sig_alrm(SIGALRM); /* отправка первого пакета */
19 iov.iov_base = recvbuf;
20 iov.iov_len = sizeof(recvbuf);
21 msg.msg_name = pr->sarecv;
22 msg.msg_iov = &iov;
23 msg.msg_iovlen = 1;
24 msg.msg_control = controlbuf;
25 for (;;) {
26 msg.msg_namelen = pr->salen;
27 msg.msg_controllen = sizeof(controlbuf);
28 n = recvmsg(sockfd, &msg, 0);
29 if (n
30 if (errno == EINTR)
31 continue;
32 else
33 err_sys("recvmsg error");
24 }
35 Gettimeofday(&tval, NULL);
36 (*pr->fproc)(recvbuf, n, &msg, &tval);
37 }
38 }
12-13
Создается символьный сокет, соответствующий выбранному протоколу. В вызове функции setuid
нашему эффективному идентификатору пользователя присваивается фактический идентификатор пользователя. Для создания символьных сокетов программа должна иметь права привилегированного пользователя, но когда символьный сокет уже создан, от этих прав можно отказаться. Всегда разумнее отказаться от лишних прав, если в них нет необходимости, например на тот случай, если в программе есть скрытая ошибка, которой кто-либо может воспользоваться.
14-15
Мы выполняем функцию инициализации для выбранного протокола. Для IPv6 такая функция представлена в листинге 28.7.
16-17
Пытаемся установить размер приемного буфера сокета, равный 61 440 байт (60×1024) — этот размер больше задаваемого по умолчанию. Это делается в расчете на случай, когда пользователь проверяет качество связи с помощью программы ping
, обращаясь либо к широковещательному адресу IPv4, либо к групповому адресу. В обоих случаях может быть получено большое количество ответов. Увеличивая размер буфера, мы уменьшаем вероятность того, что приемный буфер переполнится.
18
Запускаем обработчик сигнала, который, как мы увидим, посылает пакет и создает сигнал SIGALRM
один раз в секунду. Обычно обработчик сигналов не запускается напрямую, как у нас, но это можно делать. Обработчик сигналов является обычной функцией языка С, просто в нормальных условиях он асинхронно запускается ядром.
19-24
Мы записываем значения в неизменяемые поля структур msghdr
и iovec
, которые будут передаваться функции recvmsg
.
25-37
Основной цикл программы является бесконечным циклом, считывающим все пакеты, возвращаемые на символьный сокет ICMP. Вызывается функция gettimeofday
для регистрации времени получения пакета, а затем вызывается соответствующая функция протокола ( proc_v4
или proc_v6
) для обработки ICMP-сообщения.
В листинге 28.5 приведена функция tv_sub
, вычисляющая разность двух структур timeval
и сохраняющая результат в первой из них.
Листинг 28.5. Функция tv_sub: вычитание двух структур timeval
//lib.tv_sub.c
1 #include "unp.h"
2 void
3 tv_sub(struct timeval *out, struct timeval *in)
4 {
5 if ((out->tv_usec -= in->tv_usec)
6 --out->tv_sec;
7 out->tv_usec += 1000000;
8 }
9 out->tv_sec -= in->tv_sec;
10 }
В листинге 28.6 приведена функция proc_v4
, обрабатывающая все принимаемые сообщения ICMPv4. Можно также обратиться к рис. А.1, на котором изображен формат заголовка IPv4. Кроме того, следует осознавать, что к тому моменту, когда процесс получает на символьном сокете ICMP-сообщение, ядро уже проверило, что основные поля в заголовке IPv4 и в сообщении ICMPv4 действительны [128, с. 214, с. 311].
Листинг 28.6.Функция proc_v4: обработка сообщений ICMPv4
//ping/prov_v4.c
1 #include "ping.h"
2 void
3 proc_v4(char *ptr, ssize_t len, struct msghdr *msg, struct timeval *tvrecv)
4 {
5 int hlen1, icmplen;
6 double rtt;
7 struct ip *ip;
8 struct icmp *icmp;
9 struct timeval *tvsend;
10 ip = (struct ip*)ptr; /* начало IP-заголовка */
11 hlen1 = ip->ip_hl ip_p != IPPROTO_ICMP)
13 return; /* не ICMP */
14 icmp = (struct icmp*)(ptr + hlen1); /* начало ICMP-заголовка */
15 if ((icmplen = len - hlen1)
16 return; /* плохой пакет */
17 if (icmp->icmp_type == ICMP_ECHOREPLY) {
18 if (icmp->icmp_id != pid)
19 return; /* это не ответ на наш ECHO_REQUEST */
20 if (icmplen
21 return; /* недостаточно данных */
22 tvsend = (struct timeval*)icmp->icmp_data;
23 tv_sub(tvrecv, tvsend);
24 rtt = tvrecv->tv_sec * 1000.0 + tvrecv->tv_usec / 1000.0;
25 printf("%d bytes from %s: seq=%u, ttl=%d, rtt=%.3f ms\n",
26 icmplen, Sock_ntop_host(pr->sarecv, pr->salen),
27 icmp->icmp_seq, ip->ip_ttl, rtt);
28 } else if (verbose) {
29 printf(" %d bytes from %s: type = %d, code = %d\n",
30 icmplen, Sock_ntop_host(pr->sarecv, pr->salen),
31 icmp->icmp_type, icmp->icmp_code);
32 }
33 }
10-16
Значение поля длины заголовка IPv4, умноженное на 4, дает размер заголовка IPv4 в байтах. (Следует помнить, что IPv4-заголовок может содержать параметры.) Это позволяет нам установить указатель icmp так, чтобы он указывал на начало ICMP-заголовка. Мы проверяем, относится ли данный пакет к протоколу ICMP и имеется ли в нем достаточно данных для проверки временной отметки, включенной нами в эхо-запрос. На рис. 28.3 приведены различные заголовки, указатели и длины, используемые в коде.

Рис. 28.4. Заголовки, указатели и длина при обработке ответов ICMPv6
14-37
Если ICMP-сообщение является эхо-ответом, то чтобы убедиться, что ответ предназначен для нас, мы проверяем поле идентификатора. Если это подтверждается, то вычисляется значение RTT, которое затем выводится вместе с порядковым номером и предельным количеством транзитных узлов IPv4. Ограничение на количество транзитных узлов мы получаем из вспомогательных данных IPV6_HOPLIMIT
.
38-42
Если пользователь указал параметр командной строки -v
, выводятся также поля типа и кода всех остальных получаемых ICMP-сообщений.
Обработчиком сигнала SIGALRM является функция sig_alrm
, приведенная в листинге 28.9. В листинге 28.4 функция readloop вызывает обработчик сигнала один раз для отправки первого пакета. Эта функция в зависимости от протокола вызывает функцию send_v4
или send_v6
для отправки эхо-запроса ICMP и далее программирует запуск другого сигнала SIGALRM
через 1 с.
Листинг 28.9. Функция sig_alrm: обработчик сигнала SIGALRM
//ping/sig_alrm.c
1 #include "ping.h"
2 void
3 sig_alrm(int signo)
4 {
5 (*pr->fsend)();
6 alarm(1);
7 return;
Интервал:
Закладка: