Опубликован: 15.06.2004 | Доступ: свободный | Студентов: 2557 / 712 | Оценка: 4.35 / 3.96 | Длительность: 27:47:00
ISBN: 978-5-9556-0011-6
Лекция 11:

Сетевые средства

Признаком успешного завершения функции 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. Описание функций доступа к базе данных сетей.
Антон Коновалов
Антон Коновалов

В настоящее время актуальный стандарт - это POSIX 2008 и его дополнение POSIX 1003.13
Планируется ли актуализация материалов данного очень полезного курса?