Локальные сети персональных компьютеров. Использование протоколов IPX, SPX, NETBIOS© Александр Фролов, Григорий ФроловТом 4, М.: Диалог-МИФИ, 1993, 160 стр. 2.5. Пример c использованием ESRВ предыдущем примере при ожидании пакета мы опрашивали в цикле поле InUse блока ECB. Однако более эффективным является использование программы ESR. Эта программа получает управление тогда, когда пакет принят и поле InUse установлено в ноль. Когда ESR получает управление, регистры процессора содержат следующие значения:
Содержимое всех регистров, кроме SS и SP, а также флаги процессора записаны в стек программы. Если ESR будет обращаться к глобальным переменным программы, необходимо правильно загрузить регистр DS. Непосредственно перед вызовом ESR прерывания запрещаются. Функция ESR не возвращает никакого значения и должна завершать свою работу командой дальнего возврата RETF. Перед возвратом управления прерывания должны быть запрещены. Обычно ESR используется для установки ECB, связанных с принятыми пакетами, в очередь на обслуживание. Как и всякая программа обработки прерывания, выполняющаяся в состоянии с запрещенными прерываниями, ESR должна выполнять минимально необходимые действия и быстро возвращать управление прерванной программе. Наша программа-ESR (листинг 8) выполняет простую задачу - записывает адрес связанного с ней блока ECB в глобальную переменную completed_ecb_ptr, которая сбрасывается в главной программе перед ожиданием приема пакета. Программа-клиент (листинг 7), ожидая прихода пакета, выполняет какие-либо действия (в нашем случае она просто вводит символы с клавиатуры и выводит их на экран) и периодически опрашивает глобальную переменную. Как только пакет будет принят, в эту переменную будет записан отличный от нуля адрес блока ECB. // =================================================== // Листинг 7. Клиент IPX // Файл ipxclien.c // // (C) A. Frolov, 1993 // =================================================== #include <stdio.h> #include <stdlib.h> #include <conio.h> #include <mem.h> #include <string.h> #include <dos.h> #include "ipx.h" // Максимальный размер буфера данных #define BUFFER_SIZE 512 extern struct ECB far * completed_ecb_ptr; extern void far ipxspx_esr(void); void main(void) { // Будем работать с сокетом 0x4567 static unsigned Socket = 0x4567; // ECB для приема и передачи пакетов struct ECB RxECB, TxECB; // Заголовки принимаемых и передаваемых пакетов struct IPX_HEADER RxHeader, TxHeader; // Буферы для принимаемых и передаваемых данных unsigned char RxBuffer[BUFFER_SIZE]; unsigned char TxBuffer[BUFFER_SIZE]; printf("\n*Клиент IPX*, (C) Фролов А., 1993\n\n"); // Проверяем наличие драйвера IPX и определяем // адрес точки входа его API if(ipx_init() != 0xff) { printf("IPX не загружен!\n"); exit(-1); } // Открываем сокет, на котором будем принимать и передавать пакеты if(IPXOpenSocket(SHORT_LIVED, &Socket)) { printf("Ошибка при открытии сокета\n"); exit(-1); }; // Подготавливаем ECB для передачи пакета memset(&TxECB, 0, sizeof(TxECB)); TxECB.Socket = IntSwap(Socket); TxECB.FragmentCnt = 2; TxECB.Packet[0].Address = &TxHeader; TxECB.Packet[0].Size = sizeof(TxHeader); TxECB.Packet[1].Address = TxBuffer; TxECB.Packet[1].Size = BUFFER_SIZE; // Пакет предназначен всем станциям данной сети memset(TxECB.ImmAddress, 0xff, 6); // Подготавливаем заголовок пакета TxHeader.PacketType = 4; memset(TxHeader.DestNetwork, 0, 4); memset(TxHeader.DestNode, 0xff, 6); TxHeader.DestSocket = IntSwap(Socket); // Записываем передаваемые данные strcpy(TxBuffer, "ESR/CLIENT *DEMO*"); // Передаем пакет всем станциям в данной сети IPXSendPacket(&TxECB); completed_ecb_ptr = (unsigned long)0; // Подготавливаем ECB для приема пакета от сервера memset(&RxECB, 0, sizeof(RxECB)); RxECB.Socket = IntSwap(Socket); RxECB.FragmentCnt = 2; RxECB.Packet[0].Address = &RxHeader; RxECB.Packet[0].Size = sizeof(RxHeader); RxECB.Packet[1].Address = RxBuffer; RxECB.Packet[1].Size = BUFFER_SIZE; RxECB.ESRAddress = ipxspx_esr; IPXListenForPacket(&RxECB); printf("Ожидание ответа от сервера\n"); printf("Нажимайте любые клавиши\n"); printf("Для отмены нажмите клавишу <ESC>\n"); // Ожидаем прихода ответа от сервера while(completed_ecb_ptr == NULL) { if( getche() == 27) { IPXCloseSocket(&Socket); exit(0); } } if(RxECB.CCode == 0) { printf("\nПринят ответ от сервера '%s'\n", RxBuffer); } // Закрываем сокет IPXCloseSocket(&Socket); exit(0); } В листинге 8 приведен текст программы ESR, составленный на языке ассемблера. Программа загружает регистр DS адресом сегмента данных программы, затем записывает в глобальную переменную completed_ecb_ptr содержимое регистров ES:SI. ; =================================================== ; Листинг 8. Программа ESR ; Файл esr.asm ; ; (C) A. Frolov, 1992 ; =================================================== .286 .MODEL SMALL .DATA _completed_ecb_ptr dd 0 .CODE PUBLIC _ipxspx_esr PUBLIC _completed_ecb_ptr _ipxspx_esr PROC FAR mov ax, DGROUP mov ds, ax mov word ptr _completed_ecb_ptr+2, es mov word ptr _completed_ecb_ptr, si retf _ipxspx_esr ENDP end |