Продолжая использовать наш сайт, вы даете согласие на обработку файлов cookie, которые обеспечивают правильную работу сайта. Благодаря им мы улучшаем сайт!
Принять и закрыть

Читать, слущать книги онлайн бесплатно!

Электронная Литература.

Бесплатная онлайн библиотека.

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


Помоги проекту - поделись книгой:

■ Три средних аргумента функции select (раздел 6.3).

■ Аргумент «длина» для функции getsockopt (см. раздел 7.2).

■ Элементы msg_namelen и msg_controllen структуры msghdr при использовании с функцией recvmsg (см. раздел 14.5).

■ Элемент ifc_len структуры ifconf (см. листинг 17.1).

■ Первый из двух аргументов длины в функции sysctl (см. раздел 18.4).

3.4. Функции определения порядка байтов

Рассмотрим 16-разрядное целое число, состоящее из двух байтов. Возможно два способа хранения этих байтов в памяти. Такое расположение, когда первым идет младший байт, называется прямым порядком байтов (little-endian), а когда первым расположен старший байт — обратным порядком байтов (big-endian). На рис. 3.4 показаны оба варианта.


Рис. 3.4. Прямой и обратный порядок байтов для 16-разрядного целого числа

Сверху на этом рисунке изображены адреса, возрастающие справа налево, а снизу — слева направо. Старший бит (most significant bit, MSB) является в 16-разрядном числе крайним слева, а младший бит (least significant bit, LSB) — крайним справа.

ПРИМЕЧАНИЕ

Термины «прямой порядок байтов» и «обратный порядок байтов» указывают, какой конец многобайтового значения — младший байт или старший — хранится в качестве начального адреса значения.

К сожалению, не существует единого стандарта порядка байтов, и можно встретить системы, использующие оба формата. Способ упорядочивания байтов, используемый в конкретной системе, мы называем порядком байтов узла (host byte order). Программа, представленная в листинге 3.5, выдает порядок байтов узла.

Листинг 3.5. Программа для определения порядка байтов узла

//intro/byteorder.c

 1 #include "unp.h"

 2 int

 3 main(int argc, char **argv)

 4 {

 5  union {

 6   short s;

 7   char c[sizeof(short)];

 8  } un;

 9  un.s = 0x0102;

10  printf("%s: ", CPU_VENDOR_OS);

11  if (sizeof(short) == 2) {

12   if (un.c[0] == 1 && un.c[1] == 2)

13    printf("big-endian\n");

14   else if (un.c[0] == 2 && un.c[1] == 1)

15    printf("little-endian\n");

16   else

17    printf("unknown\n");

18  } else

19   printf('sizeof(short) = %d\n", sizeof(short));

20  exit(0);

21 }

Мы помещаем двухбайтовое значение 0x0102 в переменную типа short (короткое целое) и проверяем значения двух байтов этой переменной: с[0] (адрес А на рис. 3.4) и c[1] (адрес А + 1 на рис. 3.4), чтобы определить порядок байтов.

Константа CPU_VENDOR_OS определяется программой GNU (аббревиатура «GNU» раскрывается рекурсивно — GNU's Not Unix) autoconf в процессе конфигурации, необходимой для выполнения программ из этой книги. В этой константе хранится тип центрального процессора, а также сведения о производителе и реализации операционной системы. Ниже представлены некоторые примеры вывода этой программы при запуске ее в различных системах (см. рис. 1.7).

freebsd4 % byteorder

i386-unknown-freebsd4.8: little-endian

macosx % byteorder

powerpc-apple-darwin6.6: big-endian

freebsd5 % byteorder

sparc64-unknown-freebsd5.1: big-endian

aix % byteorder

powerpc-ibm-aix5.1.0.0: big-endian

hpux % byteorder

hppa1.1-hp-ux11 11: big-endian

linux % byteorder

i586-pc-linux-gnu: little-endian

solaris % byteorder

sparc-sun-solaris2.9: big-endian

Все, что было сказано об определении порядка байтов 16-разрядного целого числа, конечно, справедливо и в отношении 32-разрядного целого.

ПРИМЕЧАНИЕ

Существуют системы, в которых возможен переход от прямого к обратному порядку байтов либо при перезапуске системы (MIPS 2000), либо в любой момент выполнения программы (Intel i860).

Разработчикам сетевых приложений приходится обрабатывать различия в определении порядка байтов, поскольку в сетевых протоколах используется сетевой порядок байтов (network byte order). Например, в сегменте TCP есть 16- разрядный номер порта и 32-разрядный адрес IPv4. Стеки отправляющего и принимающего протоколов должны согласовывать порядок, в котором передаются байты этих многобайтовых полей. Протоколы Интернета используют обратный порядок байтов.

Теоретически реализация Unix могла бы хранить поля структуры адреса сокета в порядке байтов узла, а затем выполнять необходимые преобразования при перемещении полей в заголовки протоколов и обратно, позволяя нам не беспокоиться об этом. Но исторически и с точки зрения POSIX определяется, что для некоторых полей в структуре адреса сокета порядок байтов всегда должен быть сетевым. Поэтому наша задача — выполнить преобразование из порядка байтов узла в сетевой порядок и обратно. Для этого мы используем следующие четыре функции:

#include <netinet/in.h>

uint16_t htons(uint16_t host16bitvalue);

uint32_t htonl(uint32_t host32bitvalue);

Обе функции возвращают значение, записанное в сетевом порядке байтов

uint16_t ntohs(uint16_t net16bitvalue);

uint32_t ntohl(uint32_t net32bitvalue);

Обе функции возвращают значение, записанное в порядке байтов узла

В названиях этих функций h обозначает узел, n обозначает сеть, s — тип short, l — тип long. Термины short и long являются наследием времен реализации 4.2BSD Digital VAX. Следует воспринимать s как 16-разрядное значение (например, номер порта TCP или UDP), а l — как 32-разрядное значение (например, адрес IPv4). В самом деле, в 64-разрядной системе Digital Alpha длинное целое занимает 64 разряда, а функции htonl и ntohl оперируют 32-разрядными значениями (несмотря на то, что используют тип long).

Используя эти функции, мы можем не беспокоиться о реальном порядке байтов на узле и в сети. Для преобразования порядка байтов в конкретном значении следует вызвать соответствующую функцию. В системах с таким же порядком байтов, как в протоколах Интернета (обратным), эти четыре функции обычно определяются как пустой макрос.

Мы еще вернемся к проблеме определения порядка байтов, обсуждая данные, содержащиеся в сетевом пакете, и сравнивая их с полями в заголовках протокола, в разделе 5.18 и упражнении 5.8.

Мы до сих пор не определили термин байт. Его мы будем использовать для обозначения 8 бит, поскольку практически все современные компьютерные системы используют 8-битовые байты. Однако в большинстве стандартов Интернета для обозначения 8 бит используется термин октет. Началось это на заре TCP/IP, поскольку большая часть работы выполнялась в системах типа DEC-10, в которых не применялись 8-битовые байты. Еще одно важное соглашение, принятое в стандартах Интернета, связано с порядком битов. Во многих стандартах вы можете увидеть «изображения» пакетов, подобные приведенному ниже (это первые 32 разряда заголовка IPv4 из RFC 791):

0                   1                   2                   3

0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1

+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

|Version| IHL |Type of Service|           Total Length          |

+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

В этом примере приведены четыре байта в том порядке, в котором они передаются по проводам. Крайний слева бит является наиболее значимым. Однако нумерация начинается с нуля, который соответствует как раз наиболее значимому биту. Вам необходимо получше ознакомиться с этой записью, чтобы не испытывать трудностей при чтении описаний протоколов в RFC.

ПРИМЕЧАНИЕ

Типичной ошибкой среди программистов сетевых приложений начала 80-х, разрабатывающих код на рабочих станциях Sun (Motorola 68000 с обратным порядком байтов), было забыть вызвать одну из указанных четырех функций. На этих рабочих станциях программы работали нормально, но при переходе на машины с прямым порядком байтов они переставали работать.

3.5. Функции управления байтами

Существует две группы функций, работающих с многобайтовыми полями без преобразования данных и без интерпретации их в качестве строк языка С с завершающим нулем. Они необходимы нам при обработке структур адресов сокетов, поскольку такие поля этих структур, как IP-адреса, могут содержать нулевые байты, но при этом не являются строками С. Строки с завершающим нулем обрабатываются функциями языка С, имена которых начинаются с аббревиатуры str. Эти функции подключаются с помощью файла <string.h>.

Первая группа функций, названия которых начинаются с b (от слова «byte» — «байт»), взяты из реализации 4.2BSD и все еще предоставляются практически любой системой, поддерживающей функции сокетов. Вторая группа функций, названия которых начинаются с mem (от слова «memory» — память), взяты из стандарта ANSI С и доступны в любой системе, обеспечивающей поддержку библиотеки ANSI С.

Сначала мы представим функции, которые берут начало от реализации Беркли, хотя в книге мы будем использовать только одну из них — bzero. (Дело в том, что она имеет только два аргумента и ее проще запомнить, чем функцию memset с тремя аргументами, как объяснялось в разделе 1.2.) Две другие функции, bcopy и bcmp, могут встретиться вам в существующих приложениях.

#include <strings.h>

void bzero(void *dest, size_t nbytes);

void bcopy(const void *src, void *dest, size_t nbytes);

int bcmp(const void *ptr1, const void *ptr2, size_t nbytes);

Возвращает: 0 в случае равенства, ненулевое значение в случае неравенства

ПРИМЕЧАНИЕ

Мы впервые встречаемся со спецификатором const. В приведенном примере он служит признаком того, что значения, на которые указывает указатель, то есть src, ptr1 и ptr2, не изменяются функцией. Другими словами, область памяти, на которую указывает указатель со спецификатором const, считывается функцией, но не изменяется.

Функция bzero обнуляет заданное число байтов в указанной области памяти. Мы часто используем эту функцию для инициализации структуры адреса сокета нулевым значением. Функция bcopy копирует заданное число байтов из источника в место назначения. Функция bcmp сравнивает две произвольных последовательности байтов и возвращает нулевое значение, если две байтовых строки идентичны, и ненулевое — в противном случае.

Следующие функции являются функциями ANSI С:

#include <string.h>



Поделиться книгой:

На главную
Назад