Уильям Стивенс - UNIX: разработка сетевых приложений
- Название:UNIX: разработка сетевых приложений
- Автор:
- Жанр:
- Издательство:Питер
- Год:2007
- Город:Санкт-Петербург
- ISBN:5-94723-991-4
- Рейтинг:
- Избранное:Добавить в избранное
-
Отзывы:
-
Ваша оценка:
Уильям Стивенс - UNIX: разработка сетевых приложений краткое содержание
Новое издание книги, посвященной созданию веб-серверов, клиент-серверных приложений или любого другого сетевого программного обеспечения в операционной системе UNIX, — классическое руководство по сетевым программным интерфейсам, в частности сокетам. Оно основано на трудах Уильяма Стивенса и полностью переработано и обновлено двумя ведущими экспертами по сетевому программированию. В книгу включено описание ключевых современных стандартов, реализаций и методов, она содержит большое количество иллюстрирующих примеров и может использоваться как учебник по программированию в сетях, так и в качестве справочника для опытных программистов.
UNIX: разработка сетевых приложений - читать онлайн бесплатно ознакомительный отрывок
Интервал:
Закладка:
Листинг 11.12. Функция udp_server: создание неприсоединенного сокета для сервера UDP
//lib/udp_server.c
1 #include "unp.h"
2 int
3 udp_server(const char *host, const char *serv, socklen_t *addrlenp)
4 {
5 int sockfd, n;
6 struct addrinfo hints, *res, *ressave;
7 bzero(&hints, sizeof(struct addrinfo));
8 hints.ai_flags = AI_PASSIVE;
9 hints.ai_family = AF_UNSPEC;
10 hints.ai_socktype = SOCK_DGRAM;
11 if ((n = getaddrinfo(host, serv, &hints, &res)) != 0)
12 err_quit("udp_server error for %s, %s: %s",
13 host, serv, gai_strerror(n));
14 ressave = res;
15 do {
16 sockfd = socket(res->ai_family, res->ai_socktype, res->ai_protocol);
17 if (sockfd < 0)
18 continue; /* ошибка, пробуем следующий адрес */
19 if (bind(sockfd, res->ai_addr, res->ai_addrlen) == 0)
20 break; /* успех */
21 Close(sockfd); /* ошибка при вызове функции bind, закрываем
сокет и пробуем следующий адрес */
22 } while ((res = res->ai_next) != NULL);
23 if (res == NULL) /* значение errno устанавливается при
последнем вызове функции socket() or bind() */
24 err_sys("udp_server error for %s, %s", host, serv);
25 if (addrlenp)
26 *addrlenp = res->ai_addrlen; /* возвращается размер адреса
протокола */
27 freeaddrinfo(ressave);
28 return (sockfd);
29 }
Эта функция практически идентична функции tcp_listen
, в ней нет только вызова функции listen
. Мы устанавливаем семейство адресов AF_UNSPEC
, но вызывающий процесс может использовать ту же технологию, которую мы описали при рассмотрении листинга 11.6, чтобы потребовать использование определенного протокола (IPv4 или IPv6).
Мы не устанавливаем параметр сокета SO_REUSEADDR
для сокета UDP, поскольку этот параметр сокета может допустить связывание множества сокетов с одним и тем же портом UDP на узлах, поддерживающих многоадресную передачу, как мы говорили в разделе 7.5. Поскольку у сокета UDP нет аналога состояния TIME_WAIT, свойственного сокетам TCP, нет необходимости устанавливать этот параметр при запуске сервера.
Пример: не зависящий от протокола UDP-сервер времени и даты
В листинге 11.13 представлен наш сервер времени и даты, полученный путем модификации листинга 11.8 и предназначенный для использования UDP.
Листинг 11.13. Не зависящий от протокола UDP-сервер времени и даты
//names/daytimeudpsrv2.c
1 #include "unp.h"
2 #include
3 int
4 main(int argc, char **argv)
5 {
6 int sockfd;
7 ssize_t n;
8 char buff[MAXLINE];
9 time_t ticks;
10 socklen_t addrlen, len;
11 struct sockaddr_storage cliaddr;
12 if (argc == 2)
13 sockfd = Udp_server(NULL, argv[1], &addrlen);
14 else if (argc == 3)
15 sockfd = Udp_server(argv[1], argv[2], &addrlen);
16 else
17 err_quit("usage: daytimeudpsrv [ ] ");
18 for (;;) {
19 len = sizeof(cliaddr);
20 n = Recvfrom(sockfd, buff, MAXLINE, 0, (SA*)&cliaddr, &len);
21 printf("datagram from %s\n", Sock_ntop((SA*)&cliaddr, len));
22 ticks = time(NULL);
23 snprintf(buff, sizeof(buff), "% 24s\r\n", ctime(&ticks));
24 Sendto(sockfd, buff, strlen(buff), 0, (SA*)&cliaddr, len);
25 }
26 }
11.17. Функция getnameinfo
Эта функция дополняет функцию getaddrinfo
: она получает адрес сокета и возвращает одну символьную строку с описанием узла и другую символьную строку с описанием службы. Эта функция предоставляет указанную информацию в не зависящем от протокола виде, то есть вызывающему процессу неважно, какой тип адреса протокола содержится в структуре адреса сокета, поскольку эти подробности обрабатываются функцией.
#include
int getnameinfo(const struct sockaddr * sockaddr , socklen_t addrlen , char * host ,
size_t hostlen , char * serv , size_t servlen , int flags );
Возвращает 0 в случае успешного выполнения, -1 в случае ошибки
Аргумент sockaddr
указывает на структуру адреса сокета, содержащую адрес протокола, преобразуемый в строку, удобную для человеческого восприятия, а аргумент addrlen
содержит длину этой структуры. Эта структура и ее длина обычно возвращаются любой из следующих функций: accept
, recvfrom
, getsockname
или getpeername
.
Вызывающий процесс выделяет в памяти пространство для двух строк, удобных для человеческого восприятия: аргументы host
и hostlen
определяют строку, описывающую узел, а аргументы serv
и servlen
определяют строку, которая описывает службы. Если вызывающему процессу не нужна возвращаемая строка с описанием узла, задается нулевая длина этой строки ( hostlen
). Аналогично, нулевое значение аргумента servlen
означает, что не нужно возвращать информацию о службе.
Разница между функциями sock_ntop
и getnameinfo
состоит в том, что первая не задействует DNS, а только возвращает IP-адрес и номер порта. Последняя же обычно пытается получить имя и для узла, и для службы.
В табл. 11.4 показаны шесть флагов , которые можно задать для изменения действия, выполняемого функцией getnameinfo
.
Таблица 11.4. Флаги функции getnameinfo
Константа | Описание |
---|---|
NI_DGRAM | Дейтаграммный сокет |
NI_NAMEREQD | Возвращать ошибку, если невозможно получить имя узла по его адресу |
NI_NOFQDN | Возвращать только ту часть FQDN, которая содержит имя узла |
NI_NUMERICHOST | Возвращать численное значение адреса вместо имени узла |
NI_NUMERICSCOPE | Возвращать численное значение идентификатора области |
NI_NUMERICSERV | Возвращать номер порта вместо имени службы |
■ Флаг NI_DGRAM
должен быть задан, когда вызывающий процесс знает, что работает с дейтаграммным сокетом. Причина в том, что если функции getnameinfo
задать только IP-адрес и номер порта в структуре адреса сокета, она не сможет определить протокол (TCP или UDP). Существует несколько номеров портов, которые в случае TCP задействованы для одной службы, а в случае UDP для совершенно другой. Примером может служить порт 514, используемый службой rsh
в TCP и службой syslog
в UDP.
■ Флаг NI_NAMEREQD
приводит к возвращению ошибки, если имя узла не может быть разрешено при использовании DNS. Этот флаг может использоваться серверами, которым требуется, чтобы IP-адресу клиента было сопоставлено имя узла. Затем эти серверы получают возвращаемое имя узла, вызывают функцию gethostbyname
и проверяют, совпадают ли результаты вызова этих двух функций хотя бы частично.
■ Флаг NI_NOFQDN
вызывает сокращение имени узла, отбрасывая все, что идет после первой точки. Например, если в структуре адреса сокета содержится IP-адрес 192.168.42.2, то функция gethostbyaddr
возвратит имя aix.unpbook.com
. Но если в функции getnameinfo
задан флаг NI_NOFQDN
, она возвратит в имени узла только aix
.
■ Флаг NI_NUMERICHOST
сообщает функции getnameinfo
, что не нужно вызывать DNS (поскольку это занимает некоторое время). Вместо этого возвращается численное представление IP-адреса, вероятно, при помощи вызова функции inet_ntop
. Аналогично, флаг NI_NUMERICSERV
определяет, что вместо имени службы должен быть возвращен десятичный номер порта. Обычно серверы должны задавать этот флаг, поскольку номера портов клиента, как правило, не имеют соответствующего имени службы — это динамически назначаемые порты. NI_NUMERICSCOPE
указывает на необходимость возвращения идентификатора области в численном, а не в текстовом виде.
Интервал:
Закладка: