KnigaRead.com/
KnigaRead.com » Компьютеры и Интернет » Программное обеспечение » Уильям Стивенс - UNIX: разработка сетевых приложений

Уильям Стивенс - UNIX: разработка сетевых приложений

На нашем сайте KnigaRead.com Вы можете абсолютно бесплатно читать книгу онлайн Уильям Стивенс, "UNIX: разработка сетевых приложений" бесплатно, без регистрации.
Перейти на страницу:

Для клиента UDP структура адреса сокета, заполняемая с помощью функции getaddrinfo, будет использоваться в вызове функции sendto или connect. Если клиент сообщит, что первый адрес не работает (ошибка на присоединенном сокете UDP или тайм-аут на неприсоединенном сокете), будет предпринята попытка обратиться к другому адресу.

Если клиент знает, что он обрабатывает только один тип сокета (например, клиентами Telnet и FTP обрабатываются только сокеты TCP, а клиентами TFTP — только сокеты UDP), то элементу ai_socktype структуры hints должно быть задано соответственно либо значение SOCK_STREAM, либо значение SOCK_DGRAM.

■ Типичный сервер задает службу (service), но не имя узла (hostname), и задает флаг AI_PASSIVE в структуре hints. Возвращаемая структура адреса сокета должна содержать IP-адрес, равный INADDR_ANY (для IPv4) или IN6ADDR_ANY_INIT (для IPv6). Сервер TCP затем вызывает функции 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, инициализировать ее нулем, заполнить необходимые поля, вызвать функцию getaddrinfo и затем пройти весь связный список, проверяя каждый его элемент. В последующих разделах мы предоставим более простые интерфейсы для типичных клиентов TCP и UDP и серверов, которые будем создавать в оставшейся части книги.

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

11.7. Функция gai_strerror

Ненулевые значения ошибок, возвращаемых функцией getaddrinfo, имеют названия и значения, показанные в табл. 11.2. Функция gai_strerror получает одно из этих значений в качестве аргумента и возвращает указатель на соответствующую текстовую строку с описанием ошибки.

#include <netdb.h>


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 <netdb.h>


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.

Перейти на страницу:
Прокомментировать
Подтвердите что вы не робот:*