Уильям Стивенс - UNIX: разработка сетевых приложений
- Название:UNIX: разработка сетевых приложений
- Автор:
- Жанр:
- Издательство:Питер
- Год:2007
- Город:Санкт-Петербург
- ISBN:5-94723-991-4
- Рейтинг:
- Избранное:Добавить в избранное
-
Отзывы:
-
Ваша оценка:
Уильям Стивенс - UNIX: разработка сетевых приложений краткое содержание
Новое издание книги, посвященной созданию веб-серверов, клиент-серверных приложений или любого другого сетевого программного обеспечения в операционной системе UNIX, — классическое руководство по сетевым программным интерфейсам, в частности сокетам. Оно основано на трудах Уильяма Стивенса и полностью переработано и обновлено двумя ведущими экспертами по сетевому программированию. В книгу включено описание ключевых современных стандартов, реализаций и методов, она содержит большое количество иллюстрирующих примеров и может использоваться как учебник по программированию в сетях, так и в качестве справочника для опытных программистов.
UNIX: разработка сетевых приложений - читать онлайн бесплатно ознакомительный отрывок
Интервал:
Закладка:
//names/daytimetcpcli.c
1 #include "unp.h"
2 int
3 main(int argc, char **argv)
4 {
5 int sockfd, n;
6 char recvline[MAXLINE + 1];
7 socklen_t len;
8 struct sockaddr_storage *ss;
9 if (argc != 3)
10 err_quit
11 ("usage, daytimetcpcli ");
12 sockfd = Tcp_connect(argv[1], argv[2]);
13 len = sizeof(ss);
14 Getpeername(sockfd, (SA*)&ss, &len);
15 printf("connected to %s\n", Sock_ntop_host((SA*)&ss, len));
16 while ((n = Read(sockfd, recvline, MAXLINE)) > 0) {
17 recvline[n] = 0; /* завершающий нуль */
18 Fputs(recvline, stdout);
19 }
20 exit(0);
21 }
9-11
Теперь нам требуется второй аргумент командной строки для задания либо имени службы, либо номера порта, что позволит нашей программе соединяться с другими портами.
12
Теперь весь код сокета для этого клиента выполняется функцией tcp_connect
.
13-15
Мы вызываем функцию getpeername
, чтобы получить адрес протокола сервера и вывести его. Мы делаем это для проверки протокола, используемого в примерах, которые скоро покажем.
Обратите внимание, что функция tcp_connect
не возвращает размера структуры адреса сокета, который использовался для функции connect
. Мы могли добавить еще один аргумент-указатель, чтобы получить это значение, но при создании этой функции мы стремились добиться меньшего числа аргументов, чем у функции getaddrinfo
. Поэтому мы определяем константу MAXSOCKADDR
в нашем заголовке unp.h
так, чтобы ее размер равнялся размеру наибольшей структуры адреса сокета. Обычно это размер структуры адреса доменного сокета Unix (см. раздел 14.2), немного более 100 байт. Мы выделяем в памяти пространство для структуры указанного размера и заполняем ее с помощью функции getpeername
.
Эта версия нашего клиента работает и с IPv4, и с IPv6, тогда как версия, представленная в листинге 1.1, работала только с IPv4, а версия из листинга 1.2 — только с IPv6. Сравните нашу новую версию с представленной в листинге Д.6, которую мы написали, чтобы использовать функции gethostbyname
и getservbyname
для поддержки и IPv4, и IPv6.
Сначала мы задаем имя узла, поддерживающего только IPv4:
freebsd % daytimetcpcli linux daytime
connected to 206 168.112.96
Sun Jul 27 23:06:24 2003
Затем мы задаем имя узла, поддерживающего и IPv4, и IPv6:
freebsd % daytimetcpcli aix daytime
connected to 3ffe:b80:1f8d:2:204:acff:fe17:bf38
Sun Jul 27 23:17:13 2003
Используется адрес IPv6, поскольку у узла имеется и запись типа AAAA, и запись типа А. Кроме того, функция tcp_connect
устанавливает семейство адресов AF_UNSPEC
, поэтому, как было отмечено в табл. 11.3, сначала идет поиск записей типа AAAA, и только если этот поиск неудачен, выполняется поиск записей типа А.
В следующем примере мы указываем на необходимость использования именно адреса IPv4, задавая имя узла с суффиксом -4
, что, как мы отмечали в разделе 11.2, в соответствии с принятым нами соглашением означает имя узла, который поддерживает только записи типа А:
freebsd % daytimetcpcli aix-4 daytime
connected to 192.168.42.2
Sun Jul 27 23:17:48 2003
11.13. Функция tcp_listen
Наша следующая функция, tcp_listen
, выполняет обычные шаги сервера TCP: создание сокета TCP, связывание его с заранее известным портом с помощью функции bind и разрешение приема входящих запросов через соединение. В листинге 11.6 представлен исходный код.
#include "unp.h"
int tcp_listen(const char * hostname , const char * service , socklen_t * lenptr );
В случае успешного выполнения возвращает дескриптор присоединенного сокета, в случае ошибки не возвращает ничего
Листинг 11.6. Функция tcp_listen: выполнение обычных шагов сервера TCP
//lib/tcp_listen.c
1 #include "unp.h"
2 int
3 tcp_listen(const char *host, const char *serv, socklen_t *addrlenp)
4 {
5 int listenfd, n;
6 const int on = 1;
7 struct addrinfo hints, *res, *ressave;
8 bzero(&hints, sizeof(struct addrinfo));
9 hints.ai_flags = AI_PASSIVE;
10 hints.ai_family = AF_UNSPEC;
11 hints.ai_socktype = SOCK_STREAM;
12 if ((n = getaddrinfo(host, serv, &hints, &res)) != 0)
13 err_quit("tcp_listen error for %s, %s: %s",
14 host, serv, gai_strerror(n));
15 ressave = res;
16 do {
17 listenfd =
18 socket(res->ai_family, res->ai_socktype, res->ai_protocol);
19 if (listenfd < 0)
20 continue; /* ошибка, пробуем следующий адрес */
21 Setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on));
22 if (bind(listenfd, res->ai_addr, res->ai_addrlen) == 0)
23 break; /* успех */
24 Close(listenfd); /* ошибка при вызове функции bind, закрываем
сокет и пробуем следующий адрес*/
25 } while ((res = res->ai_next) != NULL);
26 if (res == NULL) /* значение errno устанавливается при последнем
вызове функции socket() или bind() */
27 err_sys("tcp_listen error for %s, %s", host, serv);
28 Listen(listenfd, LISTENQ);
29 if (addrlenp)
30 *addrlenp = res->ai_addrlen; /* возвращает размер адреса протокола */
31 freeaddrinfo(ressave);
32 return (listenfd);
33 }
8-15
Мы инициализируем структуру addrinfo
с учетом следующих рекомендаций (элементов структуры hints
): AI_PASSIVE
, поскольку это функция для сервера, AF_UNSPEC
для семейства адресов и SOCK_STREAM
. Вспомните табл. 11.3: если имя узла не задано (что вполне нормально для сервера, который хочет связать с дескриптором универсальный адрес), то наличие значений AI_PASSIVE
и AF_UNSPEC
вызовет возвращение двух структур адреса сокета: первой для IPv6 и второй для IPv4 (в предположении, что это узел с двойным стеком).
16-24
Вызываются функции socket
и bind
. Если любой из вызовов окажется неудачным, мы просто игнорируем данную структуру addrinfo
и переходим к следующей. Как было сказано в разделе 7.5, для сервера TCP мы всегда устанавливаем параметр сокета SO_REUSEADDR
.
25-26
Если все вызовы функций socket
и bind
окажутся неудачными, мы сообщаем об ошибке и завершаем выполнение. Как и в случае с нашей функцией tcp_connect
из предыдущего раздела, мы не пытаемся возвратить ошибку из этой функции.
27
Сокет превращается в прослушиваемый сокет с помощью функции listen
.
28-31
Если аргумент addrlenp
является непустым указателем, мы возвращаем размер адресов протокола через этот указатель. Это позволяет вызывающему процессу выделять память для структуры адреса сокета, чтобы получить адрес протокола клиента из функции accept (см. также упражнение 11.7).
Пример: сервер времени и даты
В листинге 11.7 показан наш сервер времени и даты из листинга 4.2, переписанный с использованием функции tcp_listen
.
Интервал:
Закладка: