UNIX: разработка сетевых приложений
Часть 133 из 399 Информация о книге
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 }Вызов функции getaddrinfoМы инициализируем структуру8-15с учетом следующих рекомендаций (элементов структурыaddrinfo):hints, поскольку это функция для сервера,AI_PASSIVEдля семейства адресов иAF_UNSPEC. Вспомните табл. 11.3: если имя узла не задано (что вполне нормально для сервера, который хочет связать с дескриптором универсальный адрес), то наличие значенийSOCK_STREAMиAI_PASSIVEвызовет возвращение двух структур адреса сокета: первой для IPv6 и второй для IPv4 (в предположении, что это узел с двойным стеком).AF_UNSPECСоздание сокета и связывание с адресомВызываются функции16-24иsocket. Если любой из вызовов окажется неудачным, мы просто игнорируем данную структуруbindи переходим к следующей. Как было сказано в разделе 7.5, для сервера TCP мы всегда устанавливаем параметр сокетаaddrinfo.SO_REUSEADDRПроверка на наличие ошибкиЕсли все вызовы функций25-26иsocketокажутся неудачными, мы сообщаем об ошибке и завершаем выполнение. Как и в случае с нашей функциейbindиз предыдущего раздела, мы не пытаемся возвратить ошибку из этой функции.tcp_connectСокет превращается в прослушиваемый сокет с помощью функции27.listenВозвращение размера структуры адресаЕсли аргумент28-31является непустым указателем, мы возвращаем размер адресов протокола через этот указатель. Это позволяет вызывающему процессу выделять память для структуры адреса сокета, чтобы получить адрес протокола клиента из функции accept (см. также упражнение 11.7).addrlenpПример: сервер времени и даты
В листинге 11.7 показан наш сервер времени и даты из листинга 4.2, переписанный с использованием функции
.tcp_listenЛистинг 11.7. Сервер времени и даты, переписанный с использованием функции tcp_listen
//names/daytimetcpsrv1.c1 #include "unp.h"2 #include <time.h>3 int4 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 <service or port#>");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 }Ввод имени службы или номера порта в качестве аргумента командной строкиНам нужно использовать аргумент командной строки, чтобы задать либо имя службы, либо номер порта. Это упрощает проверку нашего сервера, поскольку связывание с портом 13 для сервера времени и даты требует прав привилегированного пользователя.11-12Создание прослушиваемого сокетаФункция13создает прослушиваемый сокет. В качестве третьего аргумента мы передаем нулевой указатель, потому что нам безразличен размер структуры адреса, используемого данным семейством: мы будем работать со структуройtcp_listen.sockaddr_storage