UNIX: разработка сетевых приложений
Часть 137 из 399 Информация о книге
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, но вызывающий процесс может использовать ту же технологию, которую мы описали при рассмотрении листинга 11.6, чтобы потребовать использование определенного протокола (IPv4 или IPv6).AF_UNSPECМы не устанавливаем параметр сокета
для сокета UDP, поскольку этот параметр сокета может допустить связывание множества сокетов с одним и тем же портом UDP на узлах, поддерживающих многоадресную передачу, как мы говорили в разделе 7.5. Поскольку у сокета UDP нет аналога состояния TIME_WAIT, свойственного сокетам TCP, нет необходимости устанавливать этот параметр при запуске сервера.SO_REUSEADDRПример: не зависящий от протокола UDP-сервер времени и даты
В листинге 11.13 представлен наш сервер времени и даты, полученный путем модификации листинга 11.8 и предназначенный для использования UDP.
Листинг 11.13. Не зависящий от протокола UDP-сервер времени и даты
//names/daytimeudpsrv2.c1 #include "unp.h"2 #include <time.h>3 int4 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 else17 err_quit("usage: daytimeudpsrv [ <host> ] <service or port>");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 <netdb.h>int getnameinfo(const struct sockaddr *<i>sockaddr</i>, socklen_t <i>addrlen</i>, char *<i>host</i>,size_t <i>hostlen</i>, char *<i>serv</i>, size_t <i>servlen</i>, int <i>flags</i>);<i>Возвращает 0 в случае успешного выполнения, -1 в случае ошибки</i>Аргумент
указывает на структуру адреса сокета, содержащую адрес протокола, преобразуемый в строку, удобную для человеческого восприятия, а аргументsockaddrсодержит длину этой структуры. Эта структура и ее длина обычно возвращаются любой из следующих функций:addrlen,accept,recvfromилиgetsockname.getpeernameВызывающий процесс выделяет в памяти пространство для двух строк, удобных для человеческого восприятия: аргументы
иhostопределяют строку, описывающую узел, а аргументыhostlenиservопределяют строку, которая описывает службы. Если вызывающему процессу не нужна возвращаемая строка с описанием узла, задается нулевая длина этой строки (servlen). Аналогично, нулевое значение аргументаhostlenозначает, что не нужно возвращать информацию о службе.servlen