355 500 произведений, 25 200 авторов.

Электронная библиотека книг » Уильям Ричард Стивенс » UNIX: разработка сетевых приложений » Текст книги (страница 32)
UNIX: разработка сетевых приложений
  • Текст добавлен: 17 сентября 2016, 20:42

Текст книги "UNIX: разработка сетевых приложений"


Автор книги: Уильям Ричард Стивенс


Соавторы: Эндрю М. Рудофф,Билл Феннер

Жанр:

   

ОС и Сети


сообщить о нарушении

Текущая страница: 32 (всего у книги 88 страниц) [доступный отрывок для чтения: 32 страниц]

Функция getaddrinfoрешает проблему преобразования имен узлов и имен служб в структуры адресов сокетов. В разделе 11.17 мы опишем обратную функцию getnameinfo, которая преобразует структуры адресов сокетов в имена узлов и имена служб.

11.7. Функция gai_strerror

Ненулевые значения ошибок, возвращаемых функцией 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освобождается и может использоваться для хранения какой-либо иной информации.

ПРИМЕЧАНИЕ

Создание копии только самой структуры addrinfo, а не структур, на которые она, в свою очередь, указывает, называется поверхностным копированием (shallow сору). Копирование структуры addrinfo и всех структур, на которые она указывает, называется детальным копированием (deep сору).

11.9. Функция getaddrinfo: IPv6

Стандарт 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.

11.10. Функция getaddrinfo: примеры

Теперь мы покажем некоторые примеры работы функции 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.

11.11. Функция host_serv

Наш первый интерфейс функции 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для получения информации об узле и о службе и при этом мы хотим установить соединение самостоятельно.

11.12. Функция tcp_connect

Теперь мы напишем две функции, использующие функцию 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

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).


    Ваша оценка произведения:

Популярные книги за неделю