Резидентные программы,
работающие с IPX

В прошлый раз мы написали
программу, которая передает по сети простейшее
сообщение от одной станции к другой. Программа,
особенно на принимающей станции,получилась
чрезвычайно неудобной. После ее запуска машина
переходила в состояние долгого и нудного
ожидания, и ни одна полезная программа на ней не
выполнялась. Чтобы избежать подобных неприятных
эффектов, сетевые программы, как правило,
оформляют в виде резидентных программ (TSR).
Написание таких программ само по себе дело
довольно трудное. Если Вы хотите подробнее
ознакомиться с ним, могу порекомендовать статью
"Этот безумный, безумный, безумный мир
резидентных программ" в журнале "Компьютер
пресс" (номера 4, 5 за 1992-й год).

Я же в этой статье опишу только
особенности использования такими программами
возможностей IPX. Поэтому программа, которую мы в
этот раз напишем, будет весьма несовершенной: в
ней нет средств защиты от повторной загрузки, она
не может выгружаться из памяти без перезагрузки
системы (этим, впрочем страдают и многие
коммерческие TSR), и, наконец, - верх падения для
системного программиста - она написана на Cи, а не
на ассемблере. Последнее, впрочем, может быть
даже интересно: Вы увидите, как можно вполне
корректно оформлять на Cи самые низкоуровневые
программы.

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

Основной принцип написания
резидентных программ, работающих с IPX,
заключается в применении пользовательских
программ, вызываемых IPX при регистрации
какого-либо события. Такие программы в
документации Novell называются ESR (Event Service Routine). Для
того, чтобы сообщить IPX, какую программу надо
запускать, адрес входа ESR надо записать в
соответствующее поле управляющей структуры (ECB).

ESR не является программой
обработки прерываний, в частности, ее
разработчик не обязан заботиться о сохранности
всех регистров. Однако надо учитывать, что эта
программа вызывается из программы обработки
аппаратного прерывания IPX. Поэтому желательно,
чтобы ESR срабатывала быстро и работала при
закрытых прерываниях (кстати, при ее запуске
прерывания закрыты). Так что если необходимо по
событию IPX осуществить запись на диск, ввод с
клавиатуры или еще какое-либо действие,
требующее длительного времени или открытых
прерываний, то лучше, чтобы программа ESR только
выставляла признак, который должен периодически
проверяться программой, осуществляющей основную
работу. Как это сделать, описано в упомянутой
статье.

При вызове ESR ей передаются в
качестве параметра адрес ECB (в регистровой паре
Es:Si) и признак вида управляющего блока в регистре
Al. Что обозначает последний параметр,
объясняется ниже. Пока рассматриваются
программы приема сообщений, нам этот параметр не
нужен. Передача адреса ECB удобна в том случае,
когда одна программа обрабатывает несколько
сообщений, принимаемых через разные сокеты.
Использование нескольких ECB полезно также при
организации очереди сообщений. Именно таким
образом используется эта возможность в
приведенном примере. Конечно, маловероятно, что
кто-то сумеет так быстро послать сообщения с двух
машин, что возникнет очередь (мне это не удалось),
но попытаться можно.

В прошлый раз при трансляции
программ использовалась модель Lаrge. Наэтот раз
программа приема написана для модели Smаll.
Поэтому, кстати, текст ipx.h (листинг 3). несколько
изменился. Программа ввода в новом варианте
(листинг 1) состоит из двух частей: инициализации,
которая получает управление при старте и
программы (ESR) обработки события - прихода
сообщения.

Если используется модель,
отличная от Lаrge, надо учитывать, что ESR должна
использовать длинный возврат. Поэтому она
объявлена с атрибутом fаr. С там же атрибутом
объявлены и все структуры IPX. Последнее сделано
потому, что подпрограмма при запуске не
восстанавливает сегментные регистры, а
оставляет там случайные числа. При попытке
адресовать переменную, которая определена вне ее
тела, ESR использует случайное значение
сегментного регистра данных (Ds) со всеми
вытекающими отсюда последствиями.

Программа инициализации
посылает IPX запрос на ввод сетевого сообщения
  оставляет модуль в памяти в качестве
резидентной программы. Обратите внимание на то,
что для вычисления необходимого программе
объема памяти в первых строках запоминается
начальное состояние регистров стека (Ss:Sp). Для
этого используется фрагмент, написанный на
встроенном ассемблере. Начальное значение пары
Ss:Sp определяет
верхнюю границу памяти, занимаемой модулем.
Значение нижней границы определяется положением
PSP (program segment prefix). Таким образом объем необходимой
памяти вычисляется "без затей", то есть с
избытком. Например, в памяти остается никому не
нужный PSP и сама программа инициализации.

Программа обработки события
вызывается IPX с параметрами, передача которых
описана выше. В начале программа определяет поле
ECB, которое управляло полученным сообщением. Для
установления адреса ECB
используется тот же прием, что и при определении
положения стека программой инициализации. Зная
ECB, определяем адрес буфера ввода и распечатываем
принятую информацию. Наконец, возобновляется
запрос на ввод сообщения, управляемый тем ECB,
адрес которого был определен в первых строках
программы. В отличие от текста, приведенного в
прошлой статье, я написал все обращения к IPX на
встроенном ассемблере, раз уж избежать его
применения не удалось.

TSR RECEIVER
Листинг 1. Резидентная программа приема
сообщения

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <dos.h>
#include <ipx.h>
#define TargetSocket 0x08
#define Screen 0xb800
#define StrLen 0x50
#pragma check_stack (off)
#pragma check_pointer (off)
#pragma intrinsic ( _enable, _disable )

/* Уменьшим стек */

extern unsigned _stklen=512;

/* Конец и начало модуля */

char _huge *tsrtop;
char _huge *tsrbottom;

/**/
/*
Программа приема и распечатки строки символов
(резидентный вариант)
*/

/* Переменные общего пользования */

unsigned char _far RecieveString [80]; // Принимаемая строка
IPXHeader _far Header;
ECB_2 _far My_ECB;

/* Выдача строки в видеоамять */

void out_str (char _far *OutBuf, unsigned char Collor, short Str,
short Col) {
/* Параметры: выдаваемая строка,
цвет,
строка начала выдачи,
столбец начала выдачи
*/
unsigned int ScrOff; // Смещение в буфере экрана
unsigned int i; // Счетчик символа в строке
unsigned char _far *CountPtr; //Адрес текущего символа

ScrOff = ( StrLen*Str + Col ) * 2;
i = 0;
FP_OFF (CountPtr) = ScrOff;
FP_SEG (CountPtr) = Screen;
while (OutBuf [i] != 0x00) {
CountPtr [i*2] = OutBuf [i];
CountPtr [i*2 + 1] = Collor;
i++;
}
}

void _far My_ESR () { //Резидентная программа приема
ECB_2 _far *My_ECB_ptr;
union REGS inregs, outregs;
struct SREGS segreg;

/* Распечатка строки в предпоследнюю строку
экрана */
out_str (RecieveString, 0x2f, 22, 0);

/* Переинициализация приема сообщения */
My_ECB_ptr = &My_ECB;

/* Заполнение ECB */
_asm NOP // Так надо по причине ошибки в трансляторе

My_ECB_ptr->SocketNumber = TargetSocket;
My_ECB_ptr->FragmentCount = 2;
My_ECB_ptr->FragmentAddress_1 = &Header;
My_ECB_ptr->FragmentSize_1 = header_length;
My_ECB_ptr->FragmentAddress_2 = &RecieveString;
My_ECB_ptr->FragmentSize_2 = sizeof RecieveString;
My_ECB_ptr->ESRAddress = (void _far *)(&My_ESR);

_asm {
MOV Ax, SEG My_ECB
MOV Es,Ax
MOV Si, OFFSET My_ECB
MOV Bx, IPX_listen
INT IPX_interrupt
}
}

void main () {

ECB_2 _far *My_ECB_ptr;
unsigned tsrsize; // Размер модуля
union REGS inregs, outregs;
struct SREGS segreg;

/* Вычисление размера модуля */

_asm {
MOV WORD PTR tsrtop [0], sp
MOV WORD PTR tsrtop [2], ss
};

FP_SEG (tsrbottom) = _psp;
FP_OFF (tsrbottom) = 0;

tsrsize = ((tsrtop - tsrbottom) >> 4) + 1;

/* Заполнение ECB */

My_ECB.ESRAddress = (void _far *)My_ESR;
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); exit (1); }

/* Прием сообщения */

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);

/* Остаемся резидентом */

printf ("Программа приема установлена\n");
_dos_keep (0, tsrsize);
}

Кроме указанных действий,
программа ввода указывает цветом строки номер ECB,
ответственного за прием сообщения. Это позволит
Вам изучить логику работы IPX с ECB. Можете
поэкспериментировать с различными версиями IPX:
NetWаre 2.1x, NetWаre 2.2, NetWаre 3.xx, NovellLite ... Всегда ли
получаются одинаковые результаты? Кроме двух
описанных частей, в состав программы приема
входит еще подпрограмма выдачи строки в
видеопамять дисплея. Ее структура, по-моему, ясна
из текста. Поясню для тех, кто не знает, что буфер
видеопамяти в обычном символьном режиме обычно
начинается с адреса 0xb800. В буфере по четным
адресам располагаются выводимые символы, а по
нечетным - байты, определяющие цвет символа и
фона.

Что может быть событием IPX?

В предыдущем разделе я
упоминал о событии, управляемом IPX. Из контекста,
по-моему, ясно, что приход сообщения может быть
событием IPX, а также то, что IPX может обрабатывать
и другие события. Правда, таких событий немного:
кроме уже указанного приема сообщения, это
завершение передачи сообщения (завершение
обозначает только, что сообщение успешно
отправлено, но не то, что оно принято) и истечение
заданного временного интервала (в двух
вариантах).

Обработка передачи сообщения
при помощи ESR возможна, но не очень полезна, так
как передача пакета максимального размера
происходит достаточно быстро. Лучше
использовать зацикленное ожидание (с анализом
поля InUseFlаg). Для защиты от зацикливания можно
поставить счетчик циклов ожидания. По моим
наблюдениям, даже для сравнительно медленных
плат ArcNet и на машине 33 МГц количество циклов
ожидания при нормальной работе сети не должно
превышать 0x8000. При превышении счетчиком циклов
этого значения на более или менее обычной
технике можно смело кричать "Караул !".

Пример подпрограммы передачи
пакета с зацикленным ожиданием и с контролем
продолжительности этого ожидания приведен в
листинге 2. В данном примере используется новая
функция IPX - снять событие, управляемое указанным
ECB (Cаncel). Программа передачи сообщения возвращает
код завершения. Он равен коду завершения,
формируемому IPX, если события удалось дождаться,
или 0xFC (этот код завершения соответствует
принудительному завершению процедуры при помощи
вызова Cаncel), если не удалось. Когда Вы будете
пробовать приведенную программу передачи, у Вас
могут возникнуть проблемы с имитацией ошибки
передачи (если только Ваши аппаратные средства
не отечественного производства). Для получения
качественной и надежной ошибки передачи в сети
ArcNet достаточно отключить кабель от адаптера
связи. То же для Token Ring. Но в сети Ethernet ничего столь
же простого и дешевого посоветовать не могу.

Листинг 2. Процедура
передачи сообщения

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <dos.h>
#include <ipx.h>

/* Подпрограмма посылки сообщения со сбросом
после 0x8000
безрезультатных циклов проверки завершения
передачи
Параметр: ECB
Возвращает код завершения
*/
#define TRUE 0xFF
#define FALSE 0x00
#define TA 0x8000

int sendPacket (ECB_2 My_ECB) {

int i; // Счетчик циклов ожидания
unsigned char CancelSgn; // Признак зависания передачи

union REGS inregs, outregs;
struct SREGS segreg;

/* Начальные присвоения */

i = 0;
CancelSgn = TRUE;

/* Запрос на вывод */

inregs.x.bx = IPX_send;
inregs.x.si = FP_OFF (My_ECB);
segreg.es = FP_SEG (My_ECB);
int86x (IPX_interrupt, &inregs, &outregs, &segreg);

/* Ожидание ввода */
while ((i < TA) && (My_ECB.InUseFlag != 0x00)) {
i++;
if (My_ECB.InUseFlag == 0x00) // Передача произошла
CancelSgn = FALSE;
}

/* Если передача не произошла, снимаем запрос к IPX
*/

if (CancelSgn) {
inregs.x.bx = IPX_cancel;
inregs.x.si = FP_OFF (My_ECB);
segreg.es = FP_SEG (My_ECB);
int86x (IPX_interrupt, &inregs, &outregs, &segreg);
}
/* Передача кода завершения */
return 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

Службу времени программа IPX
поддерживает в двух вариантах: через протокол IPX
и через упрощенный протокол - Asynchronous Event Sheduller (AES).
Эти протоколы отличаются форматами ECB. Протокол
IPX использует обычный формат управляющего блока,
а протокол AES - сокращенный (специальный) формат
(листинг 3). При вызове ESR вид ECB, управлявшего
свершившимся событием, передается в качестве
параметра в регистре AL. Если использовался
протокол IPX, Al содержит 0x00, в противном случае - 0xFF.
Номер функции, передаваемый IPX в регистре Ax, равен
5 при использовании полного формата ECB (протокол
IPX) и 7 при использовании специального формата.

Протокол IPX полезно
использовать для отсчета временных интервалов в
тех случаях, когда почему-либо нужно, чтобы для
оповещения об истечении тайм-аута и для
оповещения о приеме сообщения использовался
один и тот же ECB. В остальных случаях лучше
использовать протокол AES. Пользуясь AES, Вы можете
не так беспокоиться о быстродействии своей
подпрограммы. Она может работать достаточно
долго, в том числе и при открытых прерываниях.
Самое худшее, к чему это может привести, - это к
задержке на импульс таймера сообщения о другом
временном интервале. Требования к ESR, вызываемой
из IPX, жестче: надо опасаться как потери
прерывания (если работа ведется при закрытых
прерываниях), так и путаницы из-за повторного
вызова (если ESR выполняет команду STI, то есть
разрешает прерывания).

Пример использования службы
времени дан в листинге 4. Это та же программа
приема, которая описана в первом разделе этой
статьи, но если в течение полминуты сообщение не
поступило, она оповещает сидящего за пультом об
этом прискорбном факте. Программа помещена не
полностью. То, что совпадает с программой 1,
опущено.

Листинг 4. Использование
службы времени

/* Переменные общего пользования */

unsigned char _far RecieveString_1 [80]; // Буферы ввода
unsigned char _far RecieveString_2 [80];

unsigned char _far TimeOutString [20] = "Time Out\0";

IPXHeader _far Header; // Заголовок принимаемого сообщения
ECB_2 _far My_ECB_1; // Два ECB
ECB_2 _far My_ECB_2;

SpecialECB _far ECB_TO; //Для отсчета временного интервала

. . . . . . . . . . . . . . . . . .

void _far My_ESR () { //Резидентная программа приема
ECB_2 _far *_My_ECB_ptr;
unsigned char AESFlag; // Признак сообщения службы времени
union REGS inregs, outregs;
struct SREGS segreg;

char _far * _RecieveString; // Адрес буфера ввода
unsigned char CountCollor; // Цвет строки выдачи

/* Определение ECB и буфера ввода */

_asm {
MOV WORD PTR _My_ECB_ptr[0], Si
MOV WORD PTR _My_ECB_ptr[2], Es
MOV BYTE PTR AESFlag, al
}
_RecieveString = _My_ECB_ptr -> FragmentAddress_2;

/* Распечатка в предпоследнюю строку экрана */

if (AESFlag) { // Сетевое сообщение
Обработка принятого сообщения

/* Отмена отсчета временного интервала */
_asm {
MOV Ax, SEG ECB_TO
MOV Es, Ax
MOV Si, OFFSET ECB_TO
MOV Bx, IPX_cancel
INT IPX_interrupt
};

}

else { // Обработка сообщения службы времени

out_str (TimeOutString, Collor_TO, 22, 0);
/* Заполнение ECB */
_My_ECB_ptr->ESRAddress = (void _far *)(&My_ESR);
}

/* Запрос службе времени: переустановка
временного интервала */
_asm {
MOV Ax, WORD PTR _My_ECB_ptr[2]
MOV Es,Ax
MOV Si, WORD PTR _My_ECB_ptr[0]
MOV Ax, TimeOut
MOV Bx, IPX_specialEvent
INT IPX_interrupt
};
}

void main () {
. . . . . . . . . . . . . . . . .

/* Заполнение ECB */
. . . . . . . . . . . . . . . . .

ECB_TO.ESRAddress = (void _far *)My_ESR;
. . . . . . . . . . . . . . . . .
/* Запрос службе времени */

inregs.x.bx = IPX_specialEvent;
inregs.x.ax = TimeOut;

My_ECB_ptr = &ECB_TO;

inregs.x.si = FP_OFF ( My_ECB_ptr );
segreg.es = FP_SEG ( My_ECB_ptr );
int86x (IPX_interrupt, &inregs, &outregs, &segreg);

/* Остаемся резидентом */
printf ("Программа приема установлена\n");
_dos_keep (0, tsrsize);
}

Обратите внимание, что для отсчета временного
интервала используется специальный формат ECB. В
первых строках программа наряду с адресом ECB
запоминает состояние регистра Al, которое
впоследствии используется для того, чтобы
узнать, что случилось: пришло сообщение или же
истек тайм-аут. Если пришло сообщение, то
ожидание тайм-аута снимается, а в конце программы
восстанавливается, чтобы полминуты
отсчитывались от момента прихода последнего
сообщения.

Итак, мы увидели, как
пользоваться регистрацией временных событий -
"будильником", входящим в состав службы
времени программы IPX. Но эта служба содержит и
обыкновенные "часы", на которые Ваша
программа может "посмотреть" когда захочет.
Это делается при помощи вызова GetIntervаlMаrker. Точнее
сказать, вызов GetIntervаlMаrker - это обращение не к
часам, а к "секундомеру": он возвращает не
  астрономическое время, а двухбайтовое
количество импульсов таймера с неизвестного
момента. Тем не менее, такое средство полезно для
резидентных программ, потому что позволяет
обходить нереентерабельность MS-DOS. Код функции - 8,
значение метки времени возвращается в регистре
Ax.

Прочие возможности IPX

В заключение статьи я совсем
кратко расскажу о тех возможностях IPX, о которых
не было упомянуто ранее (полный список вызовов
см. в справочном приложении к статье).

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

Следующий вызов - GetInternetworkAddress
(номер функции - 9) - позволяет программе
определить номер локального участка сети и адрес
узла, на котором она исполняется. Вызов
GetInternetworkAddress записывает в десятибайтовый буфер
сперва 4 байта номера локального участка, а затем
шестибайтовый сетевой адрес. Пример
использования этого вызова дан в листинге 5.

Листинг 5. Распечатка
сетевого адреса рабочей станции

#include <stdio.h>
#include <string.h>
#include <dos.h>
#include <ipx.h>
#define IPX_interrupt 0x7A

/*
Программа выдачи сетевого адреса своей машины
*/

/*
Шестнадцатеричная распечатка байта с лидирующим
нулем
*/

void printHH (unsigned char b) {

printf ("%1x%1x", b / 0x10, b % 0x10);

};

void main () {

struct { // Сетевой адрес
unsigned char InterNetWork [4]; // Номер локального участка
unsigned char NetworkAddress [6]; // Физический сетевой адрес
} NetAddress;

unsigned char far *Buf_ptr; // Адрес принимаемой строки

union REGS inregs, outregs;
struct SREGS segreg;

int i; // Счетчик цикла

/* Прием номера локального участка сети */

inregs.x.bx = IPX_GetInternetwork;
Buf_ptr = &NetAddress;
inregs.x.si = FP_OFF ( Buf_ptr );
segreg.es = FP_SEG ( Buf_ptr );
int86x (IPX_interrupt, &inregs, &outregs, &segreg);

/* Печать номера локального участка */

printf ("InterNetWork Address = ");

for (i = 0; i < sizeof (NetAddress.InterNetWork); i++) {
printHH (NetAddress.InterNetWork[i]);
};
printf ("\n");
/* Печать сетевого адреса адаптера */
printf ("NetworkAddress = ");
for (i = 0; i < sizeof (NetAddress.NetworkAddress); i++) {
printHH (NetAddress.NetworkAddress[i]);
};
printf ("\n");
}

Функция с номером 2 (Get Locаl Tаrget) используется для
доступа к узлам, находящимся на удаленных
локальных участках сети. Она по заданному
полному адресу узла (номер локальной сети плюс
сетевой адрес узла) определяет шестибайтовое
значение, которое надо поставить в поле ImmediаteAddress
ECB, чтобы послать сообщение. Если интересующий
Вас абонент находится на том же локальном
участке, что и Вы, функция возвратит тот же
сетевой адрес, что был указан в запросе, в
противном случае - сетевой адрес моста.

Данная функция используется
редко, так как обычно никто не знает ни полного,
ни какого-либо иного адреса узлов сети. Для
определения адресов абонентов обычно поступают
другим способом: посылают инициализационное
сообщение всем узлам (адрес broаdcаst - FF FF FF FF FF FF) и
затем собирают отклики всех возможных абонентов.
При этом в ECB приема автоматически заносится то
самое значение ImmediаteAddress, которое возвратила бы
функция Get LocаlTаrget.

Наконец, две последние функции
- Relinquish Control (0x0A) и Disconnect From Tаrget (0x0B) - и вовсе
экзотические.

Первая используется , в
основном, в многозадачных системах для того
чтобы освободить процессор. Правда, она может
понадобиться, если Ваше приложение работает на
невыделенном сервере или мосте (в этом случае DOS
имитируется средствами многозадачной ОС).

Вторая функция нужна только в
тех сетях, которые физически поддерживают
транспортный уровень. Она позволяет разорвать
соединение, установленное аппаратно.

Справочное приложение

ПЕРЕЧЕНЬ ФУНКЦИЙ IPX

Открыть сокет (Open Socket) Номер функции - 0x00.

Аргументы: Al - флаг продолжительности жизни,
Dx - номер сокета, 0 - динамически выделяемый.

Возвращаемые параметры: Al - код завершения,
Dx - номер открытого сокета.

Закрыть сокет (Close Socket) Номер функции - 0x01.

Аргументы: Dx - номер сокета.

Получить непосредственный адрес (Get Local Target) Номер
функции - 0x02.

Аргументы: Es:Si - указатель на 12-байтовый буфер
полного адреса
узла,
Es:Di - указатель на 6-байтовый буфер приема адреса.

Возвращаемые параметры: Al - код завершения,
Cx - транспортная задержка передачи пакета.

Послать пакет (Send Packet) Номер функции - 0x03.

Аргументы: Es:Si - указатель на ECB.

Инициировать прием пакета (Listen Packet) Номер функции
- 0x04.

Аргументы: Es:Si - указатель на ECB.

Задать временное событие в IPX (Shedule Event) Номер
функции - 0x05.

Аргументы: Ax - величина временного интервала в
импульсах таймера,
Es:Si - указатель на ECB полного формата.

Снять запрос (Cancel Event) Номер функции - 0x06.

Аргументы: Es:Si - указатель на ECB.

Возвращаемый параметр: Ax - код завершения.

Задать временное событие в AES (Shedule Special Event) Номер
функции - 0x07.

Аргументы: Ax - величина временного интервала в
импульсах таймера,
Es:Si - указатель на ECB полного формата.

Получить значение интервального
таймера(GetIntervalMarker) Номер функции-0x08.

Возвращаемый параметр: Ax - значение таймера.

Получить свой сетевой адрес (GetInternetworkAddress) Номер
функции - 0x09.

Аргументы: Es:Si - указатель на 12-байтовый буфер
приема адреса.

Передать управление (Relinquish Control) Номер функции -
0x0A.

Разорвать связь (Disconnect From Target) Номер функции - 0x0B.

  • Подпишись на наc в Telegram!

    Только важные новости и лучшие статьи

    Подписаться

  • Подписаться
    Уведомить о
    0 комментариев
    Межтекстовые Отзывы
    Посмотреть все комментарии