UNIX: разработка сетевых приложений
Если бы мы рассматривали все 64 возможных варианта сочетаний входных данных для функции
(имеется шесть входных переменных), многие сочетания оказались бы недопустимыми, а некоторые не имели бы смысла. Вместо этого рассмотрим наиболее типичные случаи.getaddrinfo■ Задание имени узла и службы. Это традиционный случай для клиента TCP и UDP. По завершении клиент TCP перебирает в цикле все возвращаемые IP-адреса, вызывая функции
иsocketдля каждого из них, пока не установится соединение или пока не будут перебраны все адреса. Мы показываем такой пример с нашей функциейconnectв листинге 11.2.tcp_connectДля клиента UDP структура адреса сокета, заполняемая с помощью функции
, будет использоваться в вызове функцииgetaddrinfoилиsendto. Если клиент сообщит, что первый адрес не работает (ошибка на присоединенном сокете UDP или тайм-аут на неприсоединенном сокете), будет предпринята попытка обратиться к другому адресу.connectЕсли клиент знает, что он обрабатывает только один тип сокета (например, клиентами Telnet и FTP обрабатываются только сокеты TCP, а клиентами TFTP — только сокеты UDP), то элементу
структурыai_socktypeдолжно быть задано соответственно либо значениеhints, либо значениеSOCK_STREAM.SOCK_DGRAM■ Типичный сервер задает службу (service), но не имя узла (hostname), и задает флаг
в структуреAI_PASSIVE. Возвращаемая структура адреса сокета должна содержать IP-адрес, равныйhints(для IPv4) илиINADDR_ANY(для IPv6). Сервер TCP затем вызывает функцииIN6ADDR_ANY_INIT,socketиbind. Если сервер хочет разместить в памяти с помощью функцииlistenдругую структуру адреса сокета, чтобы получить адрес клиента из функцииmalloc, то возвращаемое значениеacceptзадает требуемый для этого размер.ai_addrlenСервер UDP вызовет функции
,socketи затемbind. Если сервер хочет разместить в памяти с помощью функцииrecvfromдругую структуру адреса сокета, чтобы получить адрес клиента из функцииmalloc, возвращаемое значениеrecvfromтакже задает нужный размер.ai_addrlenКак и в случае типичного клиентского кода, если сервер знает, что он обрабатывает только один тип сокета, то элемент
структурыai_socktypeдолжен быть задан либо какhints, либо какSOCK_STREAM. Это позволяет избежать возвращения множества структур, с (возможно) неверным значением элементаSOCK_DGRAM.ai_socktype■ До сих пор мы демонстрировали серверы TCP, создающие один прослушиваемый сокет, и серверы UDP, создающие один сокет дейтаграмм. Это тот вариант, который подразумевался в предыдущем абзаце. Альтернативным устройством является сервер, который обрабатывает множество сокетов с помощью функции
. В этом сценарии сервер должен последовательно перебрать все структуры из списка, возвращаемого функциейselect, создать по одному сокету для каждой структуры и вызвать функциюgetaddrinfo.selectПРИМЕЧАНИЕПроблема этой технологии состоит в том, что условие, по которому функция getaddrinfo возвращает множество структур, возникает, когда служба может обрабатываться как протоколом IPv4, так и протоколом IPv6 (см. табл. 11.3). Но эти два протокола не полностью независимы, как мы увидели в разделе 10.2, то есть если мы создаем прослушиваемый сокет IPv6 для данного порта, нет необходимости создавать для него прослушиваемый сокет IPv4, поскольку соединения, приходящие от клиентов IPv4, автоматически обрабатываются стеком протоколов и прослушиваемым сокетом IPv6, при условии, что параметр сокета IPV6_V6ONLY не установлен.
Невзирая на тот факт, что функция
«лучше», чем функцииgetaddrinfoиgethostbyname(помимо того что эта функция упрощает написание кода, не зависящего от протокола, она обрабатывает и имя узла, и имя службы, и к тому же вся возвращаемая ею информация размещается в памяти динамически, а не статически), ее все же не так просто использовать, как это могло показаться. Проблема в том, что нам требуется разместить в памяти структуруgethostbyaddr, инициализировать ее нулем, заполнить необходимые поля, вызвать функциюhintsи затем пройти весь связный список, проверяя каждый его элемент. В последующих разделах мы предоставим более простые интерфейсы для типичных клиентов TCP и UDP и серверов, которые будем создавать в оставшейся части книги.getaddrinfoФункция
решает проблему преобразования имен узлов и имен служб в структуры адресов сокетов. В разделе 11.17 мы опишем обратную функциюgetaddrinfo, которая преобразует структуры адресов сокетов в имена узлов и имена служб.getnameinfo11.7. Функция gai_strerror
Ненулевые значения ошибок, возвращаемых функцией
, имеют названия и значения, показанные в табл. 11.2. Функцияgetaddrinfoполучает одно из этих значений в качестве аргумента и возвращает указатель на соответствующую текстовую строку с описанием ошибки.gai_strerror#include <netdb.h>char *gai_strerror(int <i>error</i>);<i>Возвращает: указатель на строку с описанием ошибки</i>Таблица 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