Текст книги "UNIX: разработка сетевых приложений"
Автор книги: Уильям Ричард Стивенс
Соавторы: Эндрю М. Рудофф,Билл Феннер
Жанр:
ОС и Сети
сообщить о нарушении
Текущая страница: 32 (всего у книги 88 страниц) [доступный отрывок для чтения: 32 страниц]
Функция getaddrinfo
решает проблему преобразования имен узлов и имен служб в структуры адресов сокетов. В разделе 11.17 мы опишем обратную функцию getnameinfo
, которая преобразует структуры адресов сокетов в имена узлов и имена служб.
Ненулевые значения ошибок, возвращаемых функцией getaddrinfo
, имеют названия и значения, показанные в табл. 11.2. Функция gai_strerror
получает одно из этих значений в качестве аргумента и возвращает указатель на соответствующую текстовую строку с описанием ошибки.
#include
char *gai_strerror(int error);
Возвращает: указатель на строку с описанием ошибки
Таблица 11.2. Ненулевые возвращаемые значения (константы) ошибок функции getaddrinfo
EAI_AGAIN | Временный сбой при попытке разрешения имен |
EAI_BADFLAGS | Недопустимое значение ai_flags |
EAI_FAIL | Неисправимая ошибка при разрешении имен |
EAI_FAMILY | Семейство ai_family не поддерживается |
EAI_MEMORY | Ошибка при выделении памяти |
EAI_NONAME | Имя узла или имя службы неизвестны или равны NULL |
EAI_OVERFLOW | Переполнен буфер пользовательских аргументов (только для getnameinfo) |
EAI_SERVICE | Запрошенная служба не поддерживается для данного типа сокета ai_socktype |
EAI_SOCKTYPE | Тип сокета ai_socktype не поддерживается |
EAI_SYSTEM | Другая системная ошибка, возвращаемая в переменной errno |
11.8. Функция freeaddrinfo
Вся память, занимаемая структурами addrinfo
, структурами ai_addr
и строкой ai_canonname
, которые возвращаются функцией getaddrinfo
, динамически выделяется функцией malloc
. Эта память освобождается при вызове функции freeaddrinfo
.
#include
void freeaddrinfo(struct addrinfo * ai);
Переменная ai
должна указывать на первую из структур addrinfo
, возвращаемых функцией getaddrinfo
. Освобождается вся область памяти, занятая структурами из связного списка, вместе с динамически выделенной областью памяти, содержащей данные, на которые указывают эти структуры (например, структуры адресов сокетов и канонические имена узлов).
Предположим, что мы вызываем функцию getaddrinfo
, проходим последовательно по всему связному списку структур addrinfo
и находим нужную структуру. Если далее мы попытаемся сохранить нужную нам информацию простым копированием структуры addrinfo
, а затем вызовем функцию freeaddrinfo
, мы получим скрытую ошибку. Причина в том, что структура addrinfo
сама указывает на динамически выделенный участок памяти (для структуры адреса сокета и, возможно, для канонического имени). Но эта область памяти, на которую указывает сохраненная нами структура, при вызове функции freeaddrinfo
освобождается и может использоваться для хранения какой-либо иной информации.
11.9. Функция getaddrinfo: IPv6ПРИМЕЧАНИЕ
Создание копии только самой структуры addrinfo, а не структур, на которые она, в свою очередь, указывает, называется поверхностным копированием (shallow сору). Копирование структуры addrinfo и всех структур, на которые она указывает, называется детальным копированием (deep сору).
Стандарт POSIX определяет как getaddrinfo
, так и возвращаемые этой функцией данные для протоколов IPv4 и IPv6. Отметим следующие моменты, прежде чем свести возвращаемые значения воедино в табл. 11.3.
■ Входные данные функции getaddrinfo
могут относиться к двум различным типам, которые выбираются в зависимости от того, какой тип структуры адреса сокета вызывающий процесс хочет получить обратно и какой тип записей нужно искать в DNS или иной базе данных.
■ Семейством адресов, указанным вызывающим процессом в структуре hints
, задается тип структуры адреса сокета, который вызывающий процесс предполагает получить. Если вызывающий процесс задает AF_INET
, функция не должна возвращать структуры sockaddr_in6
, а для AF_INET6
функция не должна возвращать структур sockaddr_in
.
■ POSIX утверждает, что при задании семейства AF_UNSPEC
должны возвращаться адреса, которые могут использоваться с любымсемейством протоколов, допускающим применение имени узла и имени службы. Это подразумевает, что если у узла имеются как записи типа AAAA, так и записи типа А, то записи типа AAAA возвращаются как структуры sockaddr_in6
, а записи типа A – как структуры sockaddr_in
. Нет смысла возвращать еще и записи типа А как адреса IPv4, преобразованные к виду IPv6, в структурах sockaddr_in6
, потому что при этом не возвращается никакой дополнительной информации – эти адреса уже возвращены в структурах sockaddr_in
.
■ Это утверждение POSIX также подразумевает, что если флаг AI_PASSIVE
задан без имени узла, то должен быть возвращен универсальный адрес IPv6 ( IN6ADDR_ANY_INIT
или 0::0) в структуре sockaddr_in6
вместе с универсальным адресом IPv4 ( INADDR_ANY
или 0.0.0.0) в структуре sockaddr_in
. Также нет смысла возвращать сначала универсальный адрес IPv4, поскольку мы увидим в разделе 12.2, что на узле с двойным стеком сокет сервера IPv6 может обрабатывать и клиенты IPv4, и клиенты IPv6.
■ Семейство адресов, указанное в поле ai_family
структуры hint
вместе с флагами AI_V4MAPPED
и AI_ALL
поля ai_flags
, задают тип записей, поиск которых ведется в DNS (тип А или тип AAAA), и тип возвращаемых адресов (IPv4, IPv6 или IPv4, преобразованные к виду IPv6). Мы обобщили это в табл. 11.3.
■ Имя узла может также быть либо шестнадцатеричной строкой IPv6, либо строкой в точечно-десятичной записи IPv4. Допустимость этой строки зависит от семейства адресов, заданного вызывающим процессом. Шестнадцатеричная строка IPv6 неприемлема, если задано семейство AF_INET
, а строка в точечно-десятичной записи IPv4 неприемлема, если задано семейство AF_INET6
. Но если задано семейство AF_UNSPEC
, то допустимы оба варианта, и при этом возвращается соответствующий тип структуры адреса сокета.
ПРИМЕЧАНИЕ
Можно возразить, что если в качестве семейства протоколов задано AF_INET6, строка в точечно-десятичной записи должна возвращаться как адрес IPv4, преобразованный к виду IPv6 в структуре sockaddr_in6. Но другим способом получения этого результата является установка префикса строки с десятичной точкой 0::ffff:.
В табл. 11.3 показано, как будут обрабатываться адреса IPv4 и IPv6 функцией getaddrinfo
. Колонка «Результат» отражает то, что мы хотим возвратить вызывающему процессу, если входные переменные таковы, как показано в первых трех колонках. Колонка «Действия» – то, каким образом мы получаем этот результат.
Таблица 11.3. Функция getaddrinfo: ее действия и результаты
Ненулевая строка с именем узла; активное или пассивное открытие | AF_UNSPEC | Имя узла | Все записи AAAA возвращаются как структуры sockaddr_in6{} и все записи А возвращаются как структуры sockaddr_in{} | Поиск по записям AAAA и поиск по записям A |
Шестнадцатеричная строка | Одна структура sockaddr_in6{} | inet_pton(AF_INET6) | ||
Строка в точечно– десятичной записи | Одна структура sockaddr_in{} | inet_pton(AF_INET) | ||
AF_INET6 | Имя узла | Все записи AAAA возвращаются как структуры sockaddr_in6{} либо все записи А возвращаются как структуры sockaddr_in6{} с адресами IPv4, преобразованными к виду IPv6 | Поиск по записям AAAA | |
Шестнадцатеричная строка | Одна структура sockaddr_in6{} | inet_pton(AF_INET6) | ||
Строка в точечно-десятичной записи | Ищется как имя узла | |||
AF_INET | Имя узла | Все записи А возвращаются как структуры sockaddr_in{} | Поиск по записям типа A | |
Шестнадцатеричная строка | Ошибка: EAI_ADDRFAMILY | |||
Строка в точечно-десятичной записи | Одна структура sockaddr_in{} | inet_pton(AF_INET) | ||
Пустая строка с именем узла; пассивное открытие | AF_UNSPEC | Неявный адрес 0::0 Неявный адрес 0.0.0.0 | Одна структура sockaddr_in6{} и одна структура sockaddr_in{} | inet_pton(AF_INET6) inet_pton(AF_INET) |
AF_INET6 | Неявный адрес 0::0 | Одна структура sockaddr_in6{} | inet_pton(AF_INET6) | |
AF_INET | Неявный адрес 0.0.0.0 | Одна структура sockaddr_in{} | inet_pton(AF_INET) | |
Пустая строка с именем узла; активное открытие | AF_UNSPEC | Неявный адрес 0::1 Неявный адрес 127.0.0.1 | Одна структура sockaddr_in6{} и одна структура sockaddr_in{} | inet_pton(AF_INET6) inet_pton(AF_INET) |
AF_INET6 | Неявный адрес 0::1 | Одна структура sockaddr_in6{} | inet_pton(AF_INET6) | |
AF_INET | Неявный адрес 127.0.0.1 | Одна структура sockaddr_in{} | inet_pton(AF_INET) |
Обратите внимание, что табл. 11.3 описывает только обработку адресов IPv4 и IPv6 функцией getaddrinfo
, то есть количество и тип адресов, возвращаемых процессу в различных ситуациях. Реальное количество структур addrinfo
зависит также от типа сокета и имени службы, о чем уже говорилось в связи с табл. 11.1.
Теперь мы покажем некоторые примеры работы функции getaddrinfo
, используя тестовую программу, которая позволяет нам вводить все параметры: имя узла, имя службы, семейство адресов, тип сокета и флаги AI_CANONNAME
и AI_PASSIVE
. (Мы не показываем эту тестовую программу, поскольку она содержит около 350 строк малоинтересного кода. Ее можно получить тем же способом, что и прочие исходные коды для этой книги.) Тестовая программа выдает информацию о переменном числе возвращаемых структур addrinfo
, показывая аргументы вызова функции socket
и адрес в каждой структуре адреса сокета. Сначала показываем тот же пример, что и на рис. 11.3:
freebsd % testga -f inet -c -h freebsd4 -s domain
socket(AF_INET, SOCK_DGRAM, 17) ai_canonname = freebsd4.unpbook.com
address: 135.197.17.100:53
socket(AF_INET, SOCK_DGRAM, 17)
address: 172:24.37.94:53
socket(AF_INET, SOCK_STREAM, 6) ai_canonname = freebsd4.unpbook.com
address: 135.197.17.100:53
socket(AF_INET, SOCK_STREAM, 6)
address: 172.24.37.94:53
Параметр -f inet
задает семейство адресов, -с указывает, что нужно возвратить каноническое имя, -h freebsd4
задает имя узла, -s domain
задает имя службы.
Типичный сценарий клиента – задать семейство адресов, тип сокета (параметр -t
), имя узла и имя службы. Следующий пример показывает это для узла с несколькими сетевыми интерфейсами с шестью адресами Ipv4:
freebsd % testga -f inet -t stream -h gateway.tuc.noao.edu -s daytime
socket(AF_INET, SOCK_STREAM, 6)
address: 140.252.108.1:13
socket(AF_INET, SOCK_STREAM, 6)
address: 140.252.1.4:13
socket(AF_INET, SOCK_STREAM, 6)
address: 140.252.104.1:13
socket(AF_INET, SOCK_STREAM, 0)
address: 140.252.3.6.13
socket(AF_INET, SOCK_STREAM, 0)
address: 140.252.4.100.13
socket(AF_INET, SOCK_STREAM, 0)
address: 140.252.1.4.13
Затем мы задаем наш узел aix
, у которого имеется и запись типа AAAA, и запись типа А, не указывая семейства адресов. Имя службы – ftp
, которая предоставляется только TCP.
freebsd % testga -h aix -s ftp -t stream
socket(AF_NET6, SOCK_STREAM, 6)
address: [3ffe:b80:1f8d:2:204:acff:fe17:bf38]:21
socket(AF_INET, SOCK_STREAM, 6)
address: 192.168.42.2:21
Поскольку мы не задали семейство адресов и запустили этот пример на узле, который поддерживает и IPv4, и IPv6, возвращаются две структуры: одна для IPv6 и одна для IPv4.
Затем мы задаем флаг AI_PASSIVE
(параметр -р
), не указываем ни семейства адресов, ни имени узла (подразумевая универсальный адрес), задаем номер порта 8888 и не указываем тип сокета.
freebsd % testga -р -s 8888 -t stream
socket(AF_INET6, SOCK_STREAM, 6)
address: [::]:8888
socket(AF_INET, SOCK_STREAM, 6)
address: 0.0.0.0:8888
Возвращаются две структуры. Поскольку мы запустили эту программу на узле, поддерживающем и IPv4, и IPv6, не задав семейства адресов, функция getaddrinfo
возвращает универсальный адрес IPv6 и универсальный адрес IPv4. Структура IPv6 возвращается перед структурой IPv4, поскольку, как мы увидим в главе 12, клиент или сервер IPv6 на узле с двойным стеком может взаимодействовать с собеседниками по IPv6 и по IPv4.
Наш первый интерфейс функции getaddrinfo
не требует от вызывающего процесса размещать в памяти структуру рекомендаций и заполнять ее. Вместо этого аргументами нашей функции host_serv
будут интересующие нас поля – семейство адресов и тип сокета.
#include "unp.h"
struct addrinfo *host_serv(const char * hostname, const char * service, int family, int socktype);
Возвращает: в случае успешного выполнения указатель на структуру addrinfo. NULL в случае ошибки
В листинге 11.3 показан исходный код этой функции.
Листинг 11.3. Функция host_serv
//lib/host_serv.c
1 #include "unp.h"
2 struct addrinfo*
3 host_serv(const char *host, const char *serv, int family, int socktype)
4 {
5 int n;
6 struct addrinfo hints, *res;
7 bzero(&hints, sizeof(struct addrinfo));
8 hints.ai_flags = AI_CANONNAME; /* всегда возвращает каноническое имя */
9 hints.ai_family = family; /* AF_UNSPEC, AF_INET, AF_INET6, ... */
10 hints.ai_socktype = socktype; /* 0, SOCK_STREAM, SOCK_DGRAM, ... */
11 if ((n = getaddrinfo(host, serv, &hints, &res)) != 0)
12 return (NULL);
13 return (res); /* возвращает указатель на первый элемент в связном
списке */
14 }
7-13
Функция инициализирует структуру рекомендаций ( hints
), вызывает функцию getaddrinfo
и возвращает пустой указатель, если происходит ошибка.
Мы вызываем эту функцию в листинге 16.11, когда нам нужно использовать getaddrinfo
для получения информации об узле и о службе и при этом мы хотим установить соединение самостоятельно.
Теперь мы напишем две функции, использующие функцию getaddrinfo
для обработки большинства сценариев клиентов и серверов TCP, которые мы создаем. Первая из этих функций, tcp_connect
, выполняет обычные шаги клиента: создание сокета TCP и соединение с сервером.
#include "unp.h"
int tcp_connect(const char * hostname, const char * service);
Возвращает: в случае успешного соединения – дескриптор присоединенного сокета, в случае ошибки не возвращается ничего
В листинге 11.4 показан исходный код.
Листинг 11.4. Функция tcp_connect: выполнение обычных шагов клиента
/ /lib/tcp_connect.c
1 #include "unp.h"
2 int
3 tcp_connect(const char *host, const char *serv)
4 {
5 int sockfd, n;
6 struct addrinfo hints, *res, *ressave;
7 bzero(&hints, sizeof(struct addrinfo));
8 hints.ai_family = AF_UNSPEC;
9 hints.ai_socktype = SOCK_STREAM;
10 if ((n = getaddrinfo(host, serv, &hints, &res)) != 0)
11 err_quit("tcp_connect error for %s, %s: %s",
12 host, serv, gai_strerror(n));
13 ressave = res;
14 do {
15 sockfd = socket(res->ai_family, res->ai_socktype, res->ai_protocol);
16 if (sockfd < 0)
17 continue; /* игнорируем этот адрес */
18 if (connect(sockfd, res->ai_addr, res->ai_addrlen) == 0)
19 break; /* успех */
20 Close(sockfd); /* игнорируем этот адрес */
21 } while ((res = res->ai_next) != NULL);
22 if (res == NULL) /* значение errno устанавливается при
последней попытке connect() */
23 err_sys("tcp_connect error for %s, %s", host, serv);
24 freeaddrinfo(ressave);
25 return (sockfd);
26 }
Вызов функции getaddrinfo
7-13
функция getaddrinfo
вызывается один раз, когда мы задаем семейство адресов AF_UNSPEC
и тип сокета SOCK_STREAM
.
Перебор всех структур addrinfo до успешного выполнения или до окончания списка
14-25
Затем пробуется каждый IP-адрес: вызываются функции socket
и connect
. Если выполнение функции socket
неудачно, это не фатальная ошибка, так как такое может случиться, если был возвращен адрес IPv6, а ядро узла не поддерживает IPv6. Если выполнение функции connect
успешно, выполняется функция break
для выхода из цикла. В противном случае, после того как перепробованы все адреса, цикл также завершается. Функция freeaddrinfo
освобождает всю динамически выделенную память.
Эта функция (как и другие наши функции, предоставляющие более простой интерфейс для функции getaddrinfo
в следующих разделах) завершается, если либо оказывается неудачным вызов функции getaddrinfo
, либо вызов функции connect
не выполняется успешно. Возвращение из нашей функции возможно лишь в случае успешного выполнения. Было бы сложно возвратить код ошибки (одну из констант EAI_ xxx
), не добавляя еще одного аргумента. Это значит, что наша функция-обертка тривиальна:
Tcp_connect(const char *host, const char *serv) {
return(tcp_connect(host, serv));
}
Тем не менее мы по-прежнему вызываем функцию-обертку вместо функции tcp_connect
ради сохранения единообразия в оставшейся части книги.
ПРИМЕЧАНИЕ
Проблема с возвращаемым значением заключается в том, что дескрипторы неотрицательные, но мы не знаем, положительны или отрицательны значения EAI_xxx. Если бы эти значения были положительными, мы могли бы возвратить равные им по абсолютной величине отрицательные значения, когда вызов функции getaddrinfo окажется неудачным. Но мы также должны возвратить некое другое отрицательное значение, чтобы указать, что все структуры были перепробованы безуспешно.
В листинге 11.5 показан наш клиент времени и даты из листинга 1.1, переписанный с использованием функции tcp_connect
.
Листинг 11.5. Клиент времени и даты, переписанный с использованием функции tcp_connect
//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 %sn", 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
Наша следующая функция, 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).