Литмир - Электронная Библиотека
Содержание  
A
A
ПРИМЕЧАНИЕ

Причина того, что sin_addr является структурой, а не просто целым числом без знака, носит исторический характер. В более ранних реализациях (например, 4.2BSD) структура in_addr определялась как объединение (union) различных структур, чтобы сделать возможным доступ к каждому из четырех байтов 32-разрядного IPv4-адреса, а также к обоим входящим в него 16-разрядным значениям. Эта возможность использовалась в адресах классов А, В и С для выборки соответствующих байтов адреса. Но с появлением подсетей и последующим исчезновением различных классов адресов (см. раздел А.4) и введением бесклассовой адресации (classless addressing) необходимость в объединении структур отпала. В настоящее время большинство систем отказались от использования объединения и просто определяют in_addr как структуру, содержащую один элемент типа in_addr_t.

■ Элемент

sin_zero
не используется, но мы всегда устанавливаем его в нуль при заполнении одной из этих структур. Перед заполнением структуры мы всегда обнуляем все ее элементы, а не только
sin_zero
.

ПРИМЕЧАНИЕ

В большинстве случаев при использовании этой структуры не требуется, чтобы элемент sin_zero был равен нулю, но, например, при привязке конкретного адреса IPv4 (а не произвольного интерфейса) этот элемент обязательно должен быть нулевым [128, с. 731-732].

■ Структуры адреса сокета используются только на данном узле: сама структура не передается между узлами, хотя определенные поля (например, поля IP-адреса и порта) используются для соединения.

Универсальная структура адреса сокета

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

Проблема в том, как объявить тип передаваемого указателя. Для ANSI С решение простое:

void*
является указателем на неопределенный (универсальный) тип (generic pointer type). Но функции сокетов существовали до появления ANSI С, и в 1982 году было принято решение определить универсальную структуру адреса сокета (generic socket address structure) в заголовочном файле
<sys/socket.h>
, которая показана в листинге 3.2.

Листинг 3.2. Универсальная структура адреса сокета: sockaddr

struct sockaddr {

 uint8_t sa_len;

 sa_family_t sa_family; /* семейство адресов: константа AF_xxx */

 char sa_data[14];      /* адрес, специфичный для протокола */

};

Функции сокетов определяются таким образом, что их аргументом является указатель на общую структуру адреса сокета, как показано в прототипе функции

bind
(ANSI С):

int bind(int, struct sockaddr*, socklen_t);

При этом требуется, чтобы для любых вызовов этих функций указатель на структуру адреса сокета, специфичную для протокола, был преобразован в указатель на универсальную структуру адреса сокета. Например:

struct sockaddr_in serv; /* структура адреса сокета IPv4 */

/* заполняем serv{} */

bind(sockfd, (struct sockaddr*)&serv, sizeof(serv));

Если мы не выполним преобразование (

struct sockaddr*
), компилятор С сгенерирует предупреждение в форме
"Warning: passing arg 2 of 'bind' from incompatible pointer type"
(Передается указатель несовместимого типа). Здесь мы предполагаем, что в системных заголовочных файлах имеется прототип ANSI С для функции bind.

С точки зрения разработчика приложений, универсальная структура адреса сокета используется только для преобразования указателей на структуры адресов конкретных протоколов.

ПРИМЕЧАНИЕ

Вспомните, что в нашем заголовочном файле unp.h (см. раздел 1.2) мы определили SA как строку "struct sockaddr", чтобы сократить код, который мы написали для преобразования этих указателей.

С точки зрения ядра основанием использовать в качестве аргументов указатели на универсальные структуры адреса сокетов является то, что ядро должно получать указатель вызывающей функции, преобразовывать его в struct sockaddr, а затем по значению элемента sa_family определять тип структуры. Но разработчику приложений было бы проще работать с указателем void*, поскольку это избавило бы его от необходимости выполнять явное преобразование указателя.

Структура адреса сокета IPv6

Структура адреса сокета IPv6 задается при помощи включения заголовочного файла

<netinet/in.h>
, как показано в листинге 3.3.

Листинг 3.3. Структура адреса сокета IPv6: sockaddr_in6

struct in6_addr {

 uint8_t s6_addr[16]; /* 128-разрядный адрес IPv6 */

                      /* сетевой порядок байтов */

};

#define SIN6_LEN /* требуется для проверки во время компиляции */

struct sockaddr_in6 {

 uint8_t sin_len;           /* длина этой структуры (24) */

 sa_family_t sin6_family;   /* AF_INET6 */

 in_port_t sin6_port;       /* номер порта транспортного уровня */

                            /* сетевой порядок байтов */

 uint32_t sin6_flowinfo;    /* приоритет и метка потока */

                            /* сетевой порядок байтов */

 struct in6_addr sin6_addr; /* IPv6-адрес */

                            /* сетевой порядок байтов */

 uint32_t sin6_scope_id;    /* набор интерфейсов */

};

ПРИМЕЧАНИЕ

Расширения API сокетов для IPv6 описаны в RFC 3493 [36].

Отметим следующие моменты относительно листинга 3.3:

■ Константа

SIN6_LEN
должна быть задана, если система поддерживает поле длины для структур адреса сокета.

■ Семейством IPv6 является

AF_INET6
, в то время как семейство IPv4 —
AF_INET
.

■ Элементы в структуре упорядочены таким образом, что если структура

sockaddr_in6
выровнена по 64 битам, то так же выровнен и 128-разрядный элемент
sin6_addr
. На некоторых 64-разрядных процессорах доступ к данным с 64-разрядными значениями оптимизирован, если данные выровнены так, что их адрес кратен 64.

27
{"b":"225366","o":1}