Рудофф Эндрю М. - UNIX: разработка сетевых приложений. 3-е изд. стр 26.

Шрифт
Фон

Таблица 3.1. Типы данных, требуемые POSIX

Тип данныхОписаниеЗаголовочный файл
int8_t8-разрядное целое со знаком<sys/types.h>
uint8_t8-разрядное целое без знака<sys/types.h>
int16_t16-разрядное целое со знаком<sys/types.h>
uint16_t16-разрядное целое без знака<sys/types.h>
int32_t32-разрядное целое со знаком<sys/types.h>
uint32_t32-разрядное целое без знака<sys/types.h>
sa_family_tсемейство адресов структуры адреса сокета<sys/socket.h>
socklen_tдлина структуры адреса сокета, обычно типа uint32_t<sys/socket.h>
in_addr_tIPv4-адрес, обычно типа uint32_t<netinet/in.h>
in_port_tпорт TCP или UDP, обычно типа uint16_t<netinet/in.h>

Вы также встретите типы данных u_char, u_short, u_intи u_long, которые не имеют знака. POSIX определяет их с замечанием, что они устарели. Они предоставляются в целях обратной совместимости.

И адрес IPv4, и номер порта TCP и UDP всегда хранятся в структуре в соответствии с порядком байтов, определенным в сети ( сетевой порядок байтов network byte order ). Об этом нужно помнить при использовании этих элементов (более подробно о разнице между порядком байтов узла и порядком байтов в сети мы поговорим в разделе 3.4).

К 32-разрядному адресу IPv4 можно обратиться двумя путями. Например, если serv это структура адреса сокета Интернета, то serv.sin_addrуказывает на 32-разрядный адрес IPv4 как на структуру in_addr, в то время как serv.sin_addr.s_addrуказывает на тот же 32-разрядный адрес IPv4 как на значение типа in_addr_t(обычно это 32-разрядное целое число без знака). Нужно следить за корректностью обращения к адресам IPv4, особенно при использовании их в качестве аргументов различных функций, потому что компиляторы часто передают структуры не так, как целочисленные переменные.

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

всегда

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

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

всегда любого

протоколов.

Проблема в том, как объявить тип передаваемого указателя. Для 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*, поскольку это избавило бы его от необходимости выполнять явное преобразование указателя.

Ваша оценка очень важна

0
Шрифт
Фон

Помогите Вашим друзьям узнать о библиотеке