11.13. Функция tcp_listen
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 }
Вызов функции getaddrinfo
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).
Данный текст является ознакомительным фрагментом.