В данной статье приводится
краткое описание программы симметричного обмена
информацией - IPX, входящей в состав сетевых
операционных систем семейства NetWare фирмы Novell.
Приводятся простейшие примеры использования
программного интерфейса IPX в пользовательских
программах.
1. Подробнее об известном
Если Вы когда-нибудь
устанавливали или хотя бы достаточно осмысленно
использовали сеть NetWare (известную у наших
программистов в основном как Novell - по имени
потерпевшего), то знаете, что на каждой рабочей
станции для использования сети надо запустить
так называемую оболочку (Shell), состоящую из двух
резидентных (TSR) программ, называемых IPX и NETx (где x
- номер версии MS-DOS). Кроме того, можно запустить
еще эмулятор всем известного сетевого протокола
сеансового уровня - NetBIOS. Впрочем последнее нужно
сейчас уже только для отечественных разработок,
почти все фирменные уже давно в NetBIOS не нуждаются.
Для чего же нужны IPX и NETx? В
самом грубом приближении можно сказать, что NETx
передает серверу все обращения программ к
удаленному диску, а IPX служит драйвером сетевого
адаптера. Но это не все. Для того чтобы понять все,
что делает IPX, вспомним, как структурируются
функции технического (Hardware) и программного (Software)
сетевого обеспечения. В большинстве случаев при
разработке сетей используется стандартная
модель OSI. OSI - это международный стандарт, который
отражает представления Международного
Института Стандартов о том, как должна
осуществляться связь между компьютерными
системами. В этой модели функции сетевого
обеспечения разбиваются на семь групп - уровней.
При этом чем выше уровень, тем более далекие от
техники и близкие к пользователю функции собраны
в нем. Для того чтобы описать функции каждого
уровня, а заодно и объяснить, что делает IPX,
позволю себе воспользоваться длинной цитатой из
профессиональной документации фирмы Novell.
Nota bene
Уровень 1 - физический уровень.
Этот слой имеет дело с типами аппаратуры (hardware),
используемой для передачи сообщений и данных
между станциями. (Световоды, витые пары,
коаксиальные кабели, мультиплексоры и т.д.).
Уровень 2 - передачи данных.
Этот уровень имеет дело с такими вещами, как
битовые шаблоны, маркеры, обнаружение ошибок при
передаче по кабелю между станциями. Например, token
ring и CSMA/CD (Ethernet) - это два стандартных метода,
применяемых для реализации уровня передачи
данных.
Уровень 3 - сетевой уровень.
Этот уровень решает задачи адресации и доставки
коммуникационных пакетов по сети. Примерами
протоколов этого уровня являются XNS IDP, IPX и TCP/IP.
Уровень 4 - транспортный
уровень. Задачей транспортного уровня является
обеспечение доставки информации с сетевого
уровня в правильном порядке без повторений.
Например, приложения NetWare имеют доступ к SPX для
выполнения функций данного уровня OSI.
Уровень 5 - сеансовый уровень.
Этот уровень предназначается для создания связи
между компьютерами, установки именования и
адресации. В системе NetWare задачи этого уровня
выполняет ответчик (NETx) или эмулятор NETBIOS.
Уровень 6 - представительский.
Этот уровень транслирует данные, передаваемые по
сети во внутренние данные компьютера и обратно.
Например, этот уровень переводит закодированные
данные или данные с различным порядком байтов в
родной цифровой формат машины. В NetWare этот
уровень реализуется совместно ответчиком и DOS.
Уровень 7 - прикладной. Этот
уровень является интерфейсом между сетью и
прикладным матобеспечением,
запускаемым на компьютере. Он в NetWare так же
осуществляется при взаимодействии ответчика и
DOS.
Так вот, программа IPX реализует
протоколы сетевого уровня (IPX) и транспортного
уровня (SPX) (о чем честно и сообщает при старте).
В соответствии с моделью ISO
каждый последующий уровень зависит только от
одного предшествующего. Поэтому, в частности, при
изменении технического обеспечения (на которое
возлагаются функции первых двух уровней) надо
изменять только программу IPX, но не NETx, которая
участвует в реализации уровней, начиная с
сеансового, и поэтому имеет право использовать
только возможности IPX и SPX, но не сетевого
оборудования.
Точно так же и Вы можете
использовать IPX для написания своих сетевых
программ, не зависящих от того, какие сетевые
адаптеры будут использоваться. Более того, Ваша
программа будет пригодна практически для всех
сетей, так как очень редко можно встретить
сетевую плату, для которой не было бы драйвера
NetWare (по крайней мере 2.1x).
2. Почему IPX, а не NETBIOS?
Некоторые программисты в нашей
стране и сейчас пишут свои приложения для сетей
NetWare, но используют для этого более известный
сетевой протокол - NETBIOS. Лет 5 назад это было
правильно, так как стандарт, принятый фирмой Novell -
протокол IPX - не был общепринятым, а стандарт NETBIOS
уже тогда поддерживался всеми серьезными
разработчиками сетей. Но сейчас использование
NETBIOS ведет только к снижению быстродействия и к
потере свободной памяти при работе в NetWare (как
говорилось выше, NETBIOS загружается на рабочей
станции NetWare дополнительно к сетевой оболочке).
Кроме того, при использовании эмулятора NETBIOS NetWare
(по крайней мере версий 2.1x) возникают
неприятности при одновременном обращении к нему
нескольких параллельных процессов (TSR, процессов
Модулы-2). И наконец, еще один аргумент в пользу IPX.
В отличие от NETBIOS, IPX используется не только на
рабочих станциях сети. Вызовы IPX и SPX один к одному
проходят также и на сервере и на мосте, и вообще
на любом узле сети NetWare. Значит, если Вы
допускаете возможность перевода впоследствии
Вашего приложения в задачу ( VAP или загружаемый
модуль), выполняемую на сервере, то всегда
целесообразно использовать вызовы IPX и SPX.
В большинстве случаев
использование NETBIOS обусловлено тем, что его
протокол более известен. Далее я собираюсь
изложить протокол IPX, а в следующей статье и SPX с
достаточной подробностью, чтобы уважаемые
читатели могли пользоваться этими протоколами
при программировании.
3. IPX. Принципы обмена
информацией
В этом пункте я собираюсь в
общих чертах описать протокол IPX, который
подробно будет описан в дальнейшем. Как Вы
увидите, протокол IPX и используемые в нем
структуры имеют определенную избыточность, в
некоторых случаях неудобную. Это объясняется
тем, что IPX (протокол, а не программа)
первоначально предназначался для несколько
других сетей. Он был разработан фирмой Xerox, и
затем стал применяться фирмой Novell как основной
протокол системы NetWare. При этом для соблюдения
преемственности было сохранено и то, что в сетях
NetWare не нужно.
IPX предназначен для передачи
коротких сообщений (пакетов) от одного процесса к
другому. При этом для пользователя почти никогда
не имеет значения, на одном компьютере
выполняются эти процессы или на разных (разница
только в некоторых кодах завершения).
Пакет имеет длину не более 576
байтов. Величина, прямо скажем, несколько
странная. Во всяком случае, она не соответствует,
насколько я знаю, длине физически пересылаемого
пакета ни для одного сетевого адаптера. Для сетей
ArcNet, например, максимальная длина передаваемого
за один раз физического пакета равна 512 байтам.
Поэтому максимальный "пакет" IPX на самом
деле передается по сети как два физических
пакета. Но пользователю до этого нет дела (если,
конечно, не учитывать потери скорости).
Каждый пакет IPX начинается со
служебной информации - заголовка (header),
состоящего из 30 байтов при использовании
протокола IPX, или 42 байтов при использовании
протокола SPX. Структура заголовка IPX приведена в
справочном приложении в конце статьи полными
объяснениями всех полей.
Пакет, пересылаемый программой
IPX, может располагаться в памяти передающей
машины не обязательно непрерывно. Можно
разделить его на несколько кусков (общая длина,
конечно, не должна превосходить знаменитых 576
байтов). Так же и буфер приема может быть разбит
на несколько частей. На практике удобно
разбивать пакет на две части: в одной хранить
заголовок пакета, а в другой - полезную (для
Вашего
приложения) информацию. Более сложные приложения
могут иметь собственную служебную информацию
для каждого пакета. В этом случае ее тоже может
оказаться полезным хранить отдельно.
Как уже говорилось, передача в
IPX происходит не от машины к машине, а от процесса
к процессу. При этом на одном компьютере могут
быть несколько процессов, которые одновременно
ждут пакета. Для приема сообщений используются
хранимые внутри IPX и недоступные программисту
структуры - сокеты (гнезда). Каждому сокету
присваивается уникальный номер. Для того чтобы
принять сообщение, задача должна сначала открыть
сокет с помощью специального запроса IPX, а затем
послать IPX другой запрос - на ввод через открытый
сокет. Передающая задача, в свою очередь, обязана
указать в заголовке номер сокета, в который она
направляет сообщение. Если принимающая сторона
не успевает принимать сообщения, образуется
очередь сообщений к сокету. Можно сделать и
наоборот: заставить несколько процессов
принимать информацию через один и тот же сокет. В
этом случае мы получим очередь процессов. Правда,
так делать плохо. Обычно организация очереди
процессов на однопроцессорной машине приводит
только к очень сильной путанице.
4. Как передать сообщение
Начнем подробное изучение IPX с
самой простой задачи - передать строку с одного
компьютера на другой и распечатать на дисплее.
Более содержательные программы будут помещены в
следующей главе, в продолжение этой статьи.
В настоящее время Novell
поставляет для разработчиков полный программный
интерфейс (API) своей системы для всех основных
вариантов языка C. Однако эти библиотеки не
продаются за рубли, и следовательно вряд ли
доступны читателю. Поэтому будем писать
программу, не пользуясь фирменными библиотеками,
а используя интерфейс с IPX через программные
прерывания.
Все функции IPX и SPX на рабочей
станции доступны через прерывание 7Ah. Номер
функции передается на регистре Bx. На других
регистрах - параметры, одним из которых в ряде
случаев является адрес управляющего блока IPX,
который называется ECB (Event Control Block).
Предполагаем, что нам известен
сетевой адрес того узла, которому адресуем
посылку. Сокет принимающего сокета - 800
(по-нормальному 00 08 - припомним, что в сети сначала
идет старший байт). Также будем считать, что нам
известны сетевые адреса передающей и
принимающей станций (для определенности - 0x02 и 0x05
соответственно).
Надо написать две программы -
для передачи сообщения и для приема сообщения.
Начнем с более простой - передающей. Для передачи
служит функция с номером 3. На паре регистров Es:Si
передается адрес ECB.
Программа должна ввести
строку, сформировать ECB и пакет, а затем передать
его. Буфер, из которого формируется пакет, не
обязан располагаться в памяти в виде
непрерывного массива, а может быть разделен на
некоторое количество кусков (см. справочное
приложение). Я использовал это свойство и
разделил буфер вывода на две части: системный
заголовок IPX и массив с передаваемой информацией.
Остальное ясно из текста программы.
Текст программы приема
приведен в листинге 1. Она должна открыть сокет и
затем принять пакет. Для ожидания приема пакета
используется динамическое ожидание: программа
проверяет флаг использования ECB. Прием, как и
передача, осуществляется в буфер, разделенный на
две части: заголовок и передаваемую строку. По
окончании программа закрывает сокет.
Листинг 1. Программа приема
сообщения
#include <stdio.h>
#include <string.h>
#include <dos.h>
#include <e:\article\progs\ipx.h>
#define TargetSocket 0x08
/*
Программа приема и распечатки строки символов
*/
void main () {
unsigned char RecieveString [80]; // Принимаемая строка
IPXHeader Header;
ECB_2 My_ECB;
ECB_2 _far *My_ECB_ptr;
union REGS inregs, outregs;
struct SREGS segreg;
/* Заполнение ECB */
My_ECB.ESRAddress = NULL;
My_ECB.SocketNumber = TargetSocket;
My_ECB.FragmentCount = 2;
My_ECB.FragmentAddress_1 = &Header;
My_ECB.FragmentSize_1 = header_length;
My_ECB.FragmentAddress_2 = &RecieveString;
My_ECB.FragmentSize_2 = sizeof RecieveString;
/* Открытие сокета */
inregs.x.bx = IPX_open;
inregs.h.al = 0xFF;
inregs.x.dx = TargetSocket;
int86 (IPX_interrupt, &inregs, &outregs);
/* Выдача кода завершения */
if (outregs.h.al != 0x00)
printf ("Ошибка открытия сокета. Код завершения %x
\n",
outregs.h.al);
/* Прием сообщения */
inregs.x.bx = IPX_listen;
My_ECB_ptr = &My_ECB;
inregs.x.si = FP_OFF ( My_ECB_ptr );
segreg.es = FP_SEG ( My_ECB_ptr );
int86x (IPX_interrupt, &inregs, &outregs, &segreg);
while (My_ECB.InUseFlag) // Ожидание завершения приема
{};
/* Распечатка строки или кода завершения */
if (My_ECB.CompletionCode != 0)
printf ("Ошибка приема сообщения. Код завершения %x
\n",
My_ECB.CompletionCode);
else
printf ("%s \n", RecieveString);
/* Закроем сокет */
inregs.x.bx = IPX_close;
inregs.x.dx = TargetSocket;
int86 (IPX_interrupt, &inregs, &outregs);
}
Листинг 2. Программа
передачи сообщения
#include <stdio.h>
#include <string.h>
#include <dos.h>
#include <e:\article\progs\ipx.h>
#define TargetSocket 0x08
#define TargetNode 0x02
/*
Программа передачи введенной оператором строки
посредством IPX
*/
void main () {
unsigned char DispString [80]; // Строка, которую вводим с
дисплея
int Length; // Длина введенной строки
IPXHeader Header;
ECB_2 My_ECB;
ECB_2 _far *My_ECB_ptr;
union REGS inregs, outregs;
struct SREGS segreg;
/* Ввод строки */
printf ("Введите, пожалуйста, передаваемое
сообщение \n");
scanf ("%s", DispString);
Length = strlen (DispString);
/* Заполнение ECB */
My_ECB.ESRAddress = NULL;
My_ECB.SocketNumber = TargetSocket;
strnset (My_ECB.ImmediateAddress, 0x00, sizeof My_ECB.ImmediateAddress);
My_ECB.ImmediateAddress[5] = TargetNode;
My_ECB.FragmentCount = 2;
My_ECB.FragmentAddress_1 = &Header;
My_ECB.FragmentSize_1 = header_length;
My_ECB.FragmentAddress_2 = &DispString;
My_ECB.FragmentSize_2 = Length+1;
/* Заполнение заголовка сообщения */
Header.PacketType = 0x00;
strnset (Header.DestantionNetwork, 0x00, sizeof Header.DestantionNetwork);
strnset (Header.DestantionNode, 0x00, sizeof Header.DestantionNode);
Header.DestantionNode [5] = TargetNode;
Header.DestantionSocket = TargetSocket;
/* Передача сообщения */
inregs.x.bx = IPX_send;
My_ECB_ptr = &My_ECB;
inregs.x.si = FP_OFF ( My_ECB_ptr );
segreg.es = FP_SEG ( My_ECB_ptr );
int86x (IPX_interrupt, &inregs, &outregs, &segreg);
while (My_ECB.InUseFlag) // Ожидание завершения посылки
{};
/* и выдача кода завершения */
printf ("Сообщение передано, код завершения %x
\n",
My_ECB.CompletionCode);
}
Листинг 3. Константы и
структуры IPX
/*
Константы и структуры IPX
*/
#define IPX_interrupt 0x7A // Программное прерывание IPX
#define header_length 30 // Длина заголовка пакета
/* Функции IPX */
#define IPX_open 0x00
#define IPX_close 0x01
#define IPX_localTarget 0x02
#define IPX_send 0x03
#define IPX_listen 0x04
#define IPX_event 0x05
#define IPX_cancel 0x06
#define IPX_specialEvent 0x07
#define IPX_interval 0x08
#define IPX_GetInternetwork 0x09
#define IPX_control 0x0A
#define IPX_disconnect 0x0B
/* Заголовок пакета */
typedef struct{
unsigned int Checksum;
unsigned int Length;
unsigned char TransportControl;
unsigned char PacketType;
unsigned char DestantionNetwork [4];
unsigned char DestantionNode [6];
unsigned int DestantionSocket;
unsigned char SourceNetwork [4];
unsigned char SourceNode [6];
unsigned int SourceSocket;
} IPXHeader;
/* Управляющий блок с одним фрагментом данных */
typedef struct {
void _far *LinkAddress;
void _far *ESRAddress;
unsigned char InUseFlag;
unsigned char CompletionCode;
unsigned int SocketNumber;
unsigned char IPXWorkspace [4];
unsigned char DriverWorkspace [12];
unsigned char ImmediateAddress [6];
unsigned int FragmentCount;
void _far *FragmentAddress;
unsigned int FragmentSize;
} ECB_1;
/* Управляющий блок с двумя фрагментами данных */
typedef struct {
void _far *LinkAddress;
void _far *ESRAddress;
unsigned char InUseFlag;
unsigned char CompletionCode;
unsigned int SocketNumber;
unsigned char IPXWorkspace [4];
unsigned char DriverWorkspace [12];
unsigned char ImmediateAddress [6];
unsigned int FragmentCount;
void _far *FragmentAddress_1;
unsigned int FragmentSize_1;
void _far *FragmentAddress_2;
unsigned int FragmentSize_2;
} ECB_2;
/* Управляющий блок службы времени */
typedef struct {
void _far *LinkAddress;
void _far *ESRAddress;
unsigned char InUseFlag;
unsigned char WorkSpace [5];
} SpecialECB;
/* Сетевой адрес плюс непосредственный адрес узла
*/
typedef struct {
unsigned char NetworkNumber [4]; // Номер локального участка
unsigned char NodeAddress [6]; // Сетевой адрес
unsigned int Socket; // Номер сокета
unsigned char ImmediateAddress [6]; // Непосредственный адрес
} IntAddr;
Для открытия сокета
используется функция IPX с номером 0. На регистре Dx
передается номер сокета (0 - если сокет
назначается динамически). На регистре Al - флаг
"живучести". Если он равен 0, то сокет
автоматически закрывается при завершении
программы, если же - 0xFF - то сокет надо закрывать
самому. Однако мои наблюдения показали, что лучше
всегда самому закрывать сокет (автоматически он
не очень закрывается). Поэтому используем честно
флаг 0xFF. Код завершения возвращается на регистре
Al. Для приема сообщения используется функция 4. На
паре Es:Si передается адрес ECB. Наконец, для
закрытия сокета используется функция 1. На
регистре Dx передается номер сокета.
Обе программы (листинги 1 и 2)
используют описания структур и некоторых
констант из файла ipx.h (листинг 3). В этом файле
описаны некоторые функции, которые не
объясняются (и не используются) в программах,
рассмотренных в данной части статьи.
При компиляции приведенных
примеров надо учесть, что Ваш компилятор C может
втихомолку вставить между полями структуры
дополнительные байты. Это хорошо для ускорения
работы Вашей программы, но плохо для связи с
чужими программами. Используя IPX, надо
блокировать такие действия. В компиляторе Microsoft C
надо в командной строке компилятора указать
опцию /Zp - отмена выравнивания.
Теперь можете пытаться
передавать сообщение. Предупреждаю только, что я
для краткости не делал проверку того, что IPX
действительно загружен. Если это не так,
последствия могут быть неприятные (в лучшем
случае - "глухое" зависание). Поэтому я
рекомендую Вам либо самостоятельно дописать
соответствующую часть программы, либо включить
загрузку IPX в autoexec.bat.
Справочное приложение
А. Пакет IPX
Каждый пакет IPX состоит из заголовка и
информационной части. Заголовок (header)
представляет собой структуру следующего вида:
Структура заголовка IPX
Смещение Содержимое Тип
0 Checksum BYTE [2]
2 Length BYTE [2]
4 Transport Control BYTE
5 Packet Type BYTE
6 Destination Network BYTE [4]
10 Destination Node BYTE [6]
16 Destination Socket BYTE [2]
18 Source Network BYTE [4]
22 Source Node BYTE [6]
28 Source Socket BYTE [2]
Все поля high-low, то есть наиболее
значимый байт поля - первый. Это не соответствует
формату процессоров Intel, для которых наиболее
значимый последний (low-high).
Поля имеют следующий смысл:
Checksum (контрольная сумма)
Это поле включено для соответствия
оригинальному определению заголовка пакета
Ксерокса. IPX всегда устанавливает это поле в 0xFFFF.
Поскольку сетевые карты сами подсчитывают
контрольную сумму пакета, оно никому не нужно.
Length (длина)
Это поле содержит полную длину сетевого пакета,
которая равна длине заголовка плюс длина данных.
Минимальная длина пакета - 30 байтов (заголовок),
максимальная - 576 байтов (заголовок плюс данные).
Поле заполняет IPX.
Transport Control (управляющее слово моста)
Это поле используется межсетевыми мостами. IPX
всегда устанавливает его в 0 перед посылкой.
Packet Type (тип пакета)
Это поле показывает, какой тип обслуживания
предполагает или запрашивает пакет. В протоколе
фирмы Xerox определены следующие типы:
0 - Unknown Packet Type (неизвестный тип)
1 - Routing Information Packet (информация маршрутизации)
2 - Echo Packet (эхо)
3 - Error Packet (пакет обработчика ошибок)
4 - Packet Exchange Packet (пакет пакетного обменника)
5 - Sequenced Packet Protocol Packet (пакет протокола SPX)
16-31 - Экспериментальные протоколы
17 - NetWare Core Protocol (протокол ядра NetWare)
Пользователи IPX должны устанавливать Packet Type 0 или
4. SPX устанавливает 5 для пакетов его протокола.
Так написано в документации фирмы Novell. Мне однако
не удалось получить каких-либо особенностей и
при использовании других типов. Кажется, имеют
смысл только два типа: 5 (необходим при
использовании SPX) и 17.
Derstantion Network (сеть адресата)
Это поле содержит номер сети,
которой принадлежит адресуемый узел. В NetWare
каждый локальный участок (Network) глобальной сети
(Internetwork) получает от администратора сети
уникальный 4-х байтовый номер.
Если это поле равно 0, то NetWare подразумевает, что
адресуемый узел находится на том же локальном
участке сети, что и передающий, и информация не
передается через мост.
Destination Node (узел адресата)
Это поле содержит физический адрес узла,
которому передается пакет. Не все типы локальных
сетей имеют одинаковую длину адресного поля.
Ethernet использует для адресации шесть байтов, а
Omninet - один. Если
адрес занимает меньше, чем 6 байтов, то он
размещается в последних байтах, а поле
дополняется нулями.
Адрес узла FF FF FF FF FF FFh (6 байтов, содержащих FF)
служит для широковещательной (broadcast) передачи
пакета всем узлам адресуемой локальной сети.
Destination Socket (сокет адресата)
Это поле содержит сокет процесса, которому
адресовано сообщение.
Фирма Novell поддерживает список сокетов, хорошо
известных (well-known) во всем окружении NetWare.
Разработчики программного обеспечения, которые
пишут дополнительные (value-added) модули NetWare, могут
получить назначение своих сокетов у фирмы Novell.
Номера динамически распределяемых сокетов
начинаются с 4000h (это отличие от стандарта Xerox).
Номера well-known сокетов назначаются, начиная с 8000h.
Сокеты с номерами, превышающими 8000h, не должны
использоваться, если они не зарегистрированы для
приложений в фирме Novell.
Source Wetwork (исходная сеть)
Поле содержит номер локальной сети, которой
принадлежит исходный узел.
Поле может содержать ноль. Это обозначает, что
физическая локальная сеть, к которой присоединен
источник, неизвестна.
Source Node (узел источника)
IPX записывает в это поле физический адрес узла
источника.
Source Socket (сокет источника)
IPX записывает в это поле номер сокета процесса,
передавшего пакет. Процессы не должны
использовать для приема и передачи один и тот же
сокет.
К сожалению, я не нашел нигде определения сокета,
используемого для передачи. Мои наблюдения
показали, что, по-видимому, это - сокет, в котором
последний раз ждал приема передающий процесс.
Такая неопределенность делает довольно сложным
выполнение требования несовпадения сокета
приема и передачи. Я, разрабатывая свой
транспортный протокол, обошел это требование
тем, что для посылки всегда использую
фиксированный номер сокета, а для приема
подтверждения - динамически выделяемый. При
использовании SPX проблем с "наложением"
сокетов, по-видимому, не возникает.
Б. Управляющий блок IPX
Для того чтобы послать или принять пакет, Ваша
программа должна сформировать управляющую
структуру, называемую ECB (Event Control Block). ECB состоит
из двух частей: 36-байтовая порция фиксированной
длины и порция данных, длина которой зависит от
количества буферов, ассоциированных с ECB. Ниже
приведено изображение ECB и описание его полей.
Структура ECB
(ECB Structure)
Смещение Содержимое Тип Поpядок
0 Link Address BYTE [4] offset-segment
4 ESR Address BYTE [4] offset-segment
8 In Use Flag BYTE
9 Completion Code BYTE
10 Socket Number WORD hi-lo
12 IPX Workspace BYTE [4]
16 Driver Workspace BYTE [12]
28 Immediate Address BYTE [6]
34 Fragment Count WORD lo-hi
38 Fragment Address 1 BYTE [4] offset-segment
40 Fragment Size 1 WORD
42 Fragment Address 2 BYTE [4] offset-segment
46 Fragment Size 2 WORD
Link Address (поле связи)
IPX использует это поле во время работы с ECB. В
общем случае это поле используется приложениями
как поле связки в списке свободных ECB.
ESR Address (адрес ESR)
Это поле содержит адрес входной точки,
определенной в приложении программы обработки
события (Event Service Routine - ESR), которая вызывается IPX
при завершении посылки или приема сообщения.
Поскольку IPX устанавливает поля In Use Flag и Completion Code,
приложение может просто периодически опрашивать
эти поля для того, чтобы уловить конец операции.
Если ESR не используется, поле ESR Address равно нулю.
In Use Flag (флаг использования)
Поле содержит ненулевое значение, когда IPX
использует данный ECB. При получении ответа IPX
пишет сюда ноль. Возможные ненулевые значения:
FBh Событие произошло, но ECB стоит в очереди на
обслуживание.
FDh Событие обслуживается, а IPX ждет истечения
временного
интервала.
FEh IPX ждет, пока в сокет придет пакет.
FFh ECB используется для посылки пакета.
FAh ECB обрабатывается IPX.
F8h Произошла попытка передачи в то время, когда IPX
был занят.
Пакет и ECB направлены в очередь для последующей
обработки.
По приходе ответа IPX сбрасывает это поле в 0.
Completion Code (код завершения)
IPX использует это поле для индикации результата
посылки или приема. Его не надо использовать,
пока In Use Flag не установлен в ноль. Возможные
значения будут указаны при описании функций IPX.
Socket Number (номер сокета)
Это поле содержит номер сокета, с которым
ассоциирован ECB. При передаче поле содержит номер
сокета-адресата, при приеме - сокета, через
который будет осуществляться прием.
IPX Workspace (рабочая область IPX)
Это 4-байтовое поле резервируется для IPX. Оно не
должно инициализироваться и изменяться во время
использования ECB IPX. В остальное время может
использоваться приложениями.
Driver Workspace (рабочая область драйвера)
Это 12-байтовое поле резервируется для сетевого
драйвера. Оно не должно инициализироваться и
изменяться во время использования ECB IPX. В
остальное время может использоваться
приложениями.
Immediate Address (непосредственный адрес)
Это поле содержит физический адрес узла адресата
или пославшего пакет узла. В случае когда пакет
посылается в другую локальную сеть - адрес моста.
Перед посылкой пакета при помощи IPX приложение
обязано заполнить это поле. SPX само заполняет его
при установлении соединения.
Fragment Count (количество фрагментов - буферов)
Это поле содержит количество фрагментов, из
которых состоит пакет (при посылке) или в которые
он будет помещен (при приеме). Его значение должно
быть больше нуля. Значение 1 указывает, что прием
или передача производятся с использованием
одного буфера.
Fragment Descriptor (описатель фрагмента)
Fragment Descriptor идентифицирует буфер, из которого
берется или в который будет помещен кусок пакета.
ECB IPX обязано иметь по крайней мере один описатель
фрагмента (соответствующий заголовку пакета).
Описатели фрагментов образуют список описателей
фрагментов (Fragment Descriptor List).
Fragment Descriptor содержит два поля:
Address содержит адрес буфера приема/передачи.
Size содержит величину буфера.
Первый буфер, описанный в Fragment Descriptor List, должен
быть не меньше 30 байтов для IPX и 42 байтов для SPX. Он
должен вмещать полный заголовок пакета. Полная
длина пакета (сумма величин фрагментов) не должна
превосходить 576.