В настоящее время актуальный стандарт - это POSIX 2008 и его дополнение POSIX 1003.13 |
Сетевые средства
Признаком успешного завершения функции getaddrinfo() является нулевой результат. В таком случае выходной аргумент res будет ссылаться на указатель на список структур типа addrinfo (связанных полем ai_next ) - принадлежащие им значения полей ai_family, ai_socktype, ai_protocol пригодны для создания подходящих сокетов с помощью функции socket(), а значения ai_addr и ai_addrlen, в зависимости от флага AI_PASSIVE, могут служить аргументами функций connect() или bind(), применяемых к созданному сокету.
Функция freeaddrinfo() позволяет освободить память, занятую списком структур типа addrinfo и ассоциированными данными.
Функцию getnameinfo() можно считать обратной по отношению к getaddrinfo(). Аргумент sa - входной, он задает транслируемый адрес сокета ( salen - размер структуры sockaddr ). Аргументы node и service - выходные, задающие адреса областей памяти, куда помещаются, соответственно, имя узла и сервиса ; размеры этих областей ограничены значениями nodelen и servicelen.
По умолчанию предполагается, что сокет имеет тип SOCK_STREAM, а кроме того, возвращается полное доменное имя хоста. Аргумент flags позволяет изменить подразумеваемое поведение. Если задан флаг NI_DGRAM, сокет считается датаграммным. При установленном флаге NI_NOFQDN возвращается короткое имя узла. Флаги NI_NUMERICHOST и NI_NUMERICSERV предписывают возвращать числовые цепочки для адресов хоста и сервиса, соответственно.
Обратим внимание на несколько технических деталей. При работе с адресами сокетов вместо родовой структуры типа sockaddr обычно используются более специализированные (описанные в заголовочном файле <netinet/in.h> ) - sockaddr_in для адресного семейства AF_INET ( IPv4 ) и sockaddr_in6 для AF_INET6 ( IPv6 ). Первая из них, согласно стандарту POSIX-2001, должна содержать по крайней мере следующие поля (все с сетевым порядком байт ).
sa_family_t sin_family; /* AF_INET */ in_port_t sin_port; /* Номер порта */ struct in_addr sin_addr; /* IP-адрес */
Структура типа sockaddr_in6 устроена несколько сложнее; мы не будем ее рассматривать.
Структуры типов sockaddr и sockaddr_in (или sockaddr_in6 ) мысленно накладываются друг на друга, а преобразование типов между ними выполняется по мере необходимости. Отметим также, что тип in_port_t эквивалентен uint16_t, структура типа in_addr содержит по крайней мере одно поле:
in_addr_t s_addr; тип in_addr_t эквивалентен uint32_t.
Техническую роль играют и функции преобразования IP-адресов из текстового представления в числовое и наоборот (см. листинг 11.5).
#include <arpa/inet.h> in_addr_t inet_addr (const char *cp); char *inet_ntoa (struct in_addr in); int inet_pton (int af, const char *restrict src, void *restrict dst); const char *inet_ntop (int af, const void *restrict src, char *restrict dst, socklen_t size);Листинг 11.5. Описание функций преобразования IP-адресов из текстового представления в числовое и наоборот.
Первые две функции манипулируют только адресами IPv4: inet_addr() преобразует текстовую цепочку cp (адрес в стандартных точечных обозначениях) в пригодное для использования в качестве IP-адреса целочисленное значение, inet_ntoa() выполняет обратное преобразование.
Вторая пара функций по сути аналогична первой, но имеет чуть более общий характер, так как способна преобразовывать адреса в формате IPv6. Первый аргумент этих функций, af, задает адресное семейство: AF_INET для IPv4 и AF_INET6 для IPv6. Буфер, на который указывает аргумент dst функции inet_pton() (в него помещается результат преобразования - IP-адрес в числовой двоичной форме с сетевым порядком байт ), должен иметь длину не менее 32 бит для адресов IPv4 и 128 бит для IPv6.
Аргумент src функции inet_ntop(), возвращающей текстовое представление, указывает на буфер с IP-адресом в числовой форме с сетевым порядком байт. Аргумент size задает длину выходного буфера, на него указывает аргумент dst. Подходящими значениями, в зависимости от адресного семейства, могут служить INET_ADDRSTRLEN или INET6_ADDRSTRLEN.
Выше было отмечено, что преобразование значений типов uint16_t и uint32_t из хостового порядка байт в сетевой выполняется посредством функций htons() и htonl() ; функции ntohs() и ntohl() осуществляют обратную операцию (см. листинг 11.6).
#include <arpa/inet.h> uint32_t htonl (uint32_t hostlong); uint16_t htons (uint16_t hostshort); uint32_t ntohl (uint32_t netlong); uint16_t ntohs (uint16_t netshort);Листинг 11.6. Описание функций преобразования целочисленных значений из хостового порядка байт в сетевой и наоборот.
Использование функции getaddrinfo() вместе с сопутствующими техническими деталями проиллюстрируем программой, показанной в листинге 11.7. Возможные результаты ее выполнения приведены в листинге 11.8.
#include <stdio.h> #include <netdb.h> #include <arpa/inet.h> int main (void) { struct addrinfo hints = {AI_CANONNAME, AF_INET, SOCK_STREAM, IPPROTO_TCP, 0, NULL, NULL, NULL}; struct addrinfo *addr_res; if (getaddrinfo ("www", "http", &hints, &addr_res) != 0) { perror ("GETADDRINFO"); } else { printf ("Результаты для сервиса http\n"); /* Пройдем по списку возвращенных структур */ do { printf ("Адрес сокета: Порт: %d IP-адрес: %s\n", ntohs (((struct sockaddr_in *) addr_res->ai_addr)->sin_port), inet_ntoa (((struct sockaddr_in *) addr_res->ai_addr)->sin_addr)); printf ("Официальное имя хоста: %s\n", addr_res->ai_canonname); } while ((addr_res = addr_res->ai_next) != NULL); } return 0; }Листинг 11.7. Пример программы, использующей функцию getaddrinfo().
Результаты для сервиса http Адрес сокета: Порт: 80 IP-адрес: 193.232.173.1 Официальное имя хоста: t01Листинг 11.8. Возможный результат работы программы, использующей функцию getaddrinfo().
Завершая изложение серии технических моментов, укажем, что полезным дополнением к функциям getaddrinfo() и getnameinfo() является функция gai_strerror() (см. листинг 11.9). Она возвращает текстовую цепочку, расшифровывающую коды ошибок, перечисленные в заголовочном файле <netdb.h>. Стандарт POSIX-2001 специфицирует следующие коды ошибок, имена которых говорят сами за себя: EAI_AGAIN, EAI_BADFLAGS, EAI_FAIL, EAI_FAMILY, EAI_MEMORY, EAI_NONAME, EAI_OVERFLOW, EAI_SERVICE, EAI_SOCKTYPE, EAI_SYSTEM.
#include <netdb.h> const char *gai_strerror (int ecode);Листинг 11.9. Описание функции gai_strerror().
Если немного модифицировать приведенную выше программу (в листинге 11.10 показан измененный фрагмент, где имя сервиса - "HTTP" - задано большими буквами), то в стандартный протокол с помощью функции gai_strerror() будет выдано содержательное диагностическое сообщение (см. листинг 11.11). Отметим, что выдача функции perror() в данном случае невразумительна.
. . . int res; if ((res = getaddrinfo ("www", "HTTP", &hints, &addr_res)) != 0) { perror ("GETADDRINFO"); fprintf (stderr, "GETADDRINFO: %s\n", gai_strerror (res)); } else { printf ("Результаты для сервиса HTTP\n"); . . .Листинг 11.10. Модифицированный фрагмент программы, использующей функции getaddrinfo() и gai_strerror().
GETADDRINFO: No such file or directory GETADDRINFO: Servname not supported for ai_socktypeЛистинг 11.11. Диагностические сообщения от функций perror() и gai_strerror(), выданные по результатам работы функции getaddrinfo().
Наряду с базой данных хостов ( узлов сети ) поддерживается база данных сетей с аналогичной логикой работы и набором функций (см. листинг 11.12).
#include <netdb.h> void setnetent (int stayopen); struct netent *getnetent (void); struct netent *getnetbyaddr (uint32_t net, int type); struct netent *getnetbyname (const char *name); void endnetent (void);Листинг 11.12. Описание функций доступа к базе данных сетей.