Уильям Стивенс - UNIX: разработка сетевых приложений
- Название:UNIX: разработка сетевых приложений
- Автор:
- Жанр:
- Издательство:Питер
- Год:2007
- Город:Санкт-Петербург
- ISBN:5-94723-991-4
- Рейтинг:
- Избранное:Добавить в избранное
-
Отзывы:
-
Ваша оценка:
Уильям Стивенс - UNIX: разработка сетевых приложений краткое содержание
Новое издание книги, посвященной созданию веб-серверов, клиент-серверных приложений или любого другого сетевого программного обеспечения в операционной системе UNIX, — классическое руководство по сетевым программным интерфейсам, в частности сокетам. Оно основано на трудах Уильяма Стивенса и полностью переработано и обновлено двумя ведущими экспертами по сетевому программированию. В книгу включено описание ключевых современных стандартов, реализаций и методов, она содержит большое количество иллюстрирующих примеров и может использоваться как учебник по программированию в сетях, так и в качестве справочника для опытных программистов.
UNIX: разработка сетевых приложений - читать онлайн бесплатно ознакомительный отрывок
Интервал:
Закладка:
Листинг 11.7. Сервер времени и даты, переписанный с использованием функции tcp_listen
//names/daytimetcpsrv1.c
1 #include "unp.h"
2 #include
3 int
4 main(int argc, char **argv)
5 {
6 int listenfd, connfd;
7 socklen_t addrlen, len;
8 char = buff[MAXLINE];
9 time_t ticks;
10 struct sockaddr_storage cliaddr;
11 if (argc != 2)
12 err_quit("usage: daytimetcpsrv1 ");
13 listenfd = Tcp_listen(NULL, argv[1], &addrlen);
14 for (;;) {
15 len = sizeof(cliaddr);
16 connfd = Accept(listenfd, (SA*)&cliaddr, &len);
17 printf("connection from %s\n", Sock_ntop((SA*)&cliaddr, len));
18 ticks = time(NULL);
19 snprintf(buff, sizeof(buff), "%.24s\r\n", ctime(&ticks));
20 Write(connfd, buff, strlen(buff));
21 Close(connfd);
22 }
23 }
11-12
Нам нужно использовать аргумент командной строки, чтобы задать либо имя службы, либо номер порта. Это упрощает проверку нашего сервера, поскольку связывание с портом 13 для сервера времени и даты требует прав привилегированного пользователя.
13
Функция tcp_listen
создает прослушиваемый сокет. В качестве третьего аргумента мы передаем нулевой указатель, потому что нам безразличен размер структуры адреса, используемого данным семейством: мы будем работать со структурой sockaddr_storage
.
14-22
Функция accept
ждет соединения с клиентом. Мы выводим адрес клиента, вызывая функцию sock_ntop
. В случае IPv4 или IPv6 эта функция выводит IP-адрес и номер порта. Мы могли бы использовать функцию getnameinfo
(описанную в разделе 11.17), чтобы попытаться получить имя узла клиента, но это подразумевает запрос PTR в DNS, что может занять некоторое время, особенно если запрос PTR окажется неудачным. В разделе 14.8 [112] упоминается, что на занятом веб-сервере почти у 25% всех клиентов, соединяющихся с этим сервером, в DNS нет записей типа PTR. Поскольку мы не хотим, чтобы наш сервер (особенно последовательный сервер) в течение нескольких секунд ждал запрос PTR, мы просто выводим IP-адрес и порт.
Пример: сервер времени и даты с указанием протокола
В листинге 11.7 есть небольшая проблема: первый аргумент функции tcp_listen
— пустой указатель, объединенный с семейством адресов AF_UNSPEC
, который задает функция tcp_listen
, — может заставить функцию getaddrinfo
возвратить структуру адреса сокета с семейством адресов, отличным от желаемого. Например, первой на узле с двойным стеком будет возвращена структура адреса сокета для IPv6 (см. табл. 11.3), но, возможно, нам требуется, чтобы наш сервер обрабатывал только IPv4.
У клиентов такой проблемы нет, поскольку клиент должен всегда задавать либо IP-адрес, либо имя узла. Клиентские приложения обычно позволяют пользователю вводить этот параметр как аргумент командной строки. Это дает нам возможность задавать имя узла, связанное с определенным типом IP-адреса (вспомните наши имена узлов -4 и -6 в разделе 11.2), или же задавать либо строку в точечно-десятичной записи (для IPv4), либо шестнадцатеричную строку (для IPv6).
И для серверов существует простая методика, позволяющая нам указать, какой именно протокол следует использовать — IPv4 или IPv6. Для этого нужно позволить пользователю ввести либо IP-адрес, либо имя узла в качестве аргумента командной строки и передать его функции getaddrinfo
. В случае IP-адреса строка точечно-десятичной записи IPv4 отличается от шестнадцатеричной строки IPv6. Следующие вызовы функции inet_pton
оказываются либо успешными либо нет, как это показано в данном случае:
inet_pton(AF_INET, "0.0.0.0", &foo); /* успешно */
inet_pton(AF_INET, "0::0", &foo); /* неудачно*/
inet_pton(AF_INET6, "0.0.0.0", &foo); /* неудачно */
inet_pton(AF_INET6, "0::0", &foo); /* успешно */
Следовательно, если мы изменим наши серверы таким образом, чтобы они получали дополнительный аргумент, то при вводе
% server
по умолчанию мы получим IPv6 на узле с двойным стеком, но при вводе
% server 0.0.0.0
явно задается IPv4, а при вводе
% server 0::0
явно задается IPv6.
В листинге 11.8 показана окончательная версия нашего сервера времени и даты.
Листинг 11.8. Не зависящий от протокола сервер времени и даты, использующий функцию tcp_listen
names/daytimetcpsrv2.c
1 #include "unp.h"
2 #include
3 int
4 main(int argc, char **argv)
5 {
6 int listenfd, connfd;
7 socklen_t addrlen, len;
8 struct sockaddr_storage cliaddr;
9 char buff[MAXLINE];
10 time_t ticks;
11 if (argc == 2)
12 listenfd = Tcp_listen(NULL, argv[1], &addrlen);
13 else if (argc == 3)
14 listenfd = Tcp_listen(argv[1], argv[2], &addrlen);
15 else
16 err_quit("usage; daytimetcpsrv2 [ ] ");
17 for (;;) {
18 len = sizeof(cliaddr);
19 connfd = Accept(listenfd, (SA*)&cliaddr, &len);
20 printf("connection from %s\n", Sock_ntop((SA*)&cliaddr, len));
21 ticks = time(NULL);
21 snprintf(buff, sizeof(buff), "%.24s\r\n", ctime(&ticks));
23 Write(connfd, buff, strlen(buff));
24 Close(connfd);
25 }
26 }
11-16
Единственное изменение по сравнению с листингом 11.6 — это обработка аргументов командной строки, позволяющая пользователю в дополнение к имени службы или порту задавать либо имя узла, либо IP-адрес для связывания с сервером.
Сначала мы запускаем этот сервер с сокетом IPv4 и затем соединяемся с сервером от клиентов на двух различных узлах, расположенных в локальной подсети:
freebsd % daytimetcpsrv2 0.0.0.0 9999
connection from 192.168.42.2:32961
connection from 192.168.42.2:1389
А теперь мы запустим сервер с сокетом IPv6:
solaris % daytimetcpsrv2 0::0 9999
connection from [3ffe:b80:1f8d:2:204:acff:fe17:bf38]:32964
connection from [3ffe:b80:1f8d:2:230:65ff:fe15:caa7]:49601
connection from [::ffff:192:168:42:3]:32967
connection from [::ffff:192:168:42:3]:49602
Первое соединение — от узла aix
, использующего IPv6, а второе — от узла macosx
, использующего IPv6. Два следующих соединения — от узлов aix
и macosx
, но они используют IPv4, а не IPv6. Мы можем определить это, потому что оба адреса клиента, возвращаемые функцией accept
, являются адресами IPv4, преобразованными к виду IPv6.
Мы только что показали, что сервер IPv6, работающий на узле с двойным стеком, может обрабатывать как клиенты IPv4, так и клиенты IPv6. Адреса IPv4-клиента передаются серверу IPv6 как адреса IPv4, преобразованные к виду IPv6, что мы рассматривали в разделе 12.2.
11.14. Функция udp_client
Наши функции, предоставляющие более простой интерфейс для функции getaddrinfo
, в случае UDP изменяются: в этом разделе мы представляем клиентскую функцию, создающую неприсоединенный сокет UDP, а в следующем — другую функцию, создающую присоединенный сокет UDP.
#include "unp.h"
int udp_client(const char * hostname , const char * service ,
Интервал:
Закладка: