Уильям Стивенс - UNIX: разработка сетевых приложений
- Название:UNIX: разработка сетевых приложений
- Автор:
- Жанр:
- Издательство:Питер
- Год:2007
- Город:Санкт-Петербург
- ISBN:5-94723-991-4
- Рейтинг:
- Избранное:Добавить в избранное
-
Отзывы:
-
Ваша оценка:
Уильям Стивенс - UNIX: разработка сетевых приложений краткое содержание
Новое издание книги, посвященной созданию веб-серверов, клиент-серверных приложений или любого другого сетевого программного обеспечения в операционной системе UNIX, — классическое руководство по сетевым программным интерфейсам, в частности сокетам. Оно основано на трудах Уильяма Стивенса и полностью переработано и обновлено двумя ведущими экспертами по сетевому программированию. В книгу включено описание ключевых современных стандартов, реализаций и методов, она содержит большое количество иллюстрирующих примеров и может использоваться как учебник по программированию в сетях, так и в качестве справочника для опытных программистов.
UNIX: разработка сетевых приложений - читать онлайн бесплатно ознакомительный отрывок
Интервал:
Закладка:
Причина, по которой в этом примере мы используем функции sigsetjmp
и siglongjmp
, вместо того чтобы просто перехватывать ошибку EINTR
(как мы поступили в листинге 14.1), заключается в том, что библиотечные функции захвата пакетов (которые вызываются из нашей функции udp_read
) заново запускают операцию чтения в случае возвращения ошибки EINTR
. Поскольку мы не хотим модифицировать библиотечные функции, единственным решением для нас является перехватывание сигнала SIGALRM
и выполнение нелокального перехода (оператора goto
), который возвращает управление в наш код, а не в библиотечную функцию.
25-26
Функция send_dns_query
(см. листинг 29.8) отправляет запрос DNS на сервер имен. Функция dns_read
считывает ответ. Мы вызываем функцию alarm
для предотвращения «вечной» блокировки функции read
. Если истекает заданное (в секундах) время ожидания, генерируется сигнал SIGALRM
, и наш обработчик сигнала вызывает функцию siglongjmp
.
27-32
Если значение полученной контрольной суммы UDP равно нулю, это значит, что сервер не вычислил и не отправил контрольную сумму.
В листинге 29.7 показана наша функция sig_alrm
— обработчик сигнала SIGALRM
.
Листинг 29.7. Функция sig_alrm: обработка сигнала SIGALRM
//udpcksum/udpcksum.c
1 #include "udpcksum.h"
2 #include
3 static sigjmp_buf jmpbuf;
4 static int canjump;
5 void
6 sig_alrm(int signo)
7 {
8 if (canjump == 0)
9 return;
10 siglongjmp(jmpbuf, 1);
11 }
8-10
Флаг canjump
был установлен в листинге 29.6 после инициализации буфера перехода функцией sigsetjmp
. Если флаг был установлен, в результате вызова функции siglongjmp
управление осуществляется таким образом, как если бы функция sigsetjmp
из листинга 29.6 возвратила бы значение 1.
В листинге 29.8 показана функция send_dns_query
, посылающая запрос UDP на сервер DNS. Эта функция формирует запрос DNS.
Листинг 29.8. Функция send_dns_query: отправка запроса UDP на сервер DNS
//udpcksum/senddnsquery-raw.c
6 void
7 send_dns_query(void)
8 {
9 size_t nbytes;
10 char *buf, *ptr;
11 buf = Malloc(sizeof(struct udpiphdr) + 100);
12 ptr = buf + sizeof(struct udpiphdr); /* место для заголовков IP и UDP */
13 *((uint16_t*)ptr) = htons(1234); /* идентификатор */
14 ptr += 2;
15 *((uint16_t*)ptr) = htons(0x0100); /* флаги */
16 ptr += 2;
17 *((uint16_t*)ptr) = htons(1); /* количество запросов */
18 ptr += 2;
19 *((uint16_t*)ptr) = 0; /* количество записей в ответе */
20 ptr += 2;
21 *((uint16_t*)ptr) = 0; /* количество авторитетных записей */
22 ptr += 2;
23 *((uint16_t*)ptr) = 0; /* количество дополнительных записей */
24 ptr += 2;
25 memcpy(ptr, "\001a\014root-servers\003net\000", 20);
26 ptr += 20;
27 *((uint16_t*)ptr) = htons(1); /* тип запроса = А */
28 ptr += 2;
29 *((uint16_t*)ptr) = htons(1); /* класс запроса = 1 (IP-адрес) */
30 ptr += 2;
31 nbytes = (ptr - buf) - sizeof(struct udpiphdr);
32 udp_write(buf, mbytes),
33 if (verbose)
35 printf("sent: %d bytes of data\n", nbytes);
36 }
11-12
В буфере buf
имеется место для 20-байтового заголовка IP, 8-байтового заголовка UDP и еще 100 байт для пользовательских данных. Указатель ptr
установлен на первый байт пользовательских данных.
13-24
Для понимания деталей устройства дейтаграммы UDP требуется понимание формата сообщения DNS. Эту информацию можно найти в разделе 14.3 [111]. Мы присваиваем полю идентификации значение 1234, сбрасываем флаги, задаем количество запросов — 1, а затем обнуляем количество записей ресурсов (RR, resource records), получаемых в ответ, количество RR, определяющих полномочия, и количество дополнительных RR.
25-30
Затем мы формируем простой запрос, который располагается после заголовка: запрос типа А IP-адреса узла a.root-servers.net
. Это доменное имя занимает 20 байт и состоит из 4 фрагментов: однобайтовая часть a
, 12-байтовая часть root-servers
, 3-байтовая часть net
и корневая часть, длина которой занимает 0 байт. Тип запроса 1 (так называемый запрос типа А), и класс запроса также 1.
31-32
Это сообщение состоит из 36 байт пользовательских данных (восемь 2-байтовых полей и 20-байтовое доменное имя). Мы вызываем нашу функцию udp_write
для формирования заголовков UDP и IP и последующей записи дейтаграммы UDP в наш символьный сокет.
В листинге 29.9 показана функция open_output
, работающая с символьными сокетами.
Листинг 29.9. Функция open_output: подготовка символьного сокета
2 int rawfd; /* символьный сокет */
3 void
4 open_output(void)
5 {
6 int on=1;
7 /*
8 * Для отправки IP-дейтаграмм нужен символьный сокет
9 * Для его создания нужны права привилегированного пользователя.
10 * Кроме того, необходимо указать параметр сокета IP_HDRINCL.
11 */
12 rawfd = Socket(dest->sa_family, SOCK_RAW, 0);
13 Setsockopt(rawfd, IPPROTO_IP, IP_HDRINCL, &on., sizeof(on));
14 }
2
Мы объявляем глобальную переменную, в которой будет храниться дескриптор символьного сокета.
7-13
Мы создаем символьный сокет и включаем параметр сокета IP_HDRINCL
. Это позволяет нам формировать IP-дейтаграммы целиком, включая заголовок IP.
В листинге 29.10 показана наша функция udp_write
, которая формирует заголовки IP и UDP, а затем записывает дейтаграмму в символьный сокет.
Листинг 29.10. Функция udp_write: формирование заголовков UDP и IP и запись дейтаграммы IP в символьный сокет
//udpcksum/udpwrite.c
19 void
20 udp_write(char *buf, int userlen)
21 {
22 struct udpiphdr *ui;
23 struct ip *ip;
24 /* заполнение заголовка и вычисление контрольной суммы */
25 ip = (struct ip*)buf;
26 ui = (struct udpiphdr*)buf;
27 bzero(ui, sizeof(*ui));
28 /* добавляем 8 к длине псевдозаголовка */
29 ui->ui_len = htons((uint16_t)(sizeof(struct udphdr) + userlen));
30 /* добавление 28 к длине IP-дейтаграммы */
31 userlen += sizeof(struct udpiphdr);
32 ui->ui_pr = IPPROTO_UDP;
33 ui->ui_src.s_addr = ((struct sockaddr_in*)local)->sin_addr.s_addr;
34 ui->ui_dst.s_addr = ((struct sockaddr_in*)dest)->sin_addr.s_addr;
35 ui->ui_sport = ((struct sockaddr_in*)local)->sin_port;
36 ui->ui_dport = ((struct sockaddr_in*)dest)->sin_port;
37 ui->ui_ulen = ui->ui_len;
38 if (zerosum == 0) {
39 #if 1 /* заменить на if 0 для Solaris 2.x. x
40 if ((ui->ui_sum = m_cksum((u_int16_t*)in, userlen)) == 0)
41 ui->ui_sum = 0xffff;
42 #else
43 ui->ui_sum = ui->ui_len;
44 #endif
45 }
46 /* заполнение оставшейся части IP-заголовка */
Интервал:
Закладка: