В качестве примера рассмотрим две программы. Первая из них является сервером, вторая - клиентом.
После запуска сервер ожидает пакет от клиента. В свою очередь, клиент после запуска посылает пакет одновременно всем станциям данной сети, поэтому на какой бы станции ни работал сервер, он обязательно примет пакет от клиента.
Когда сервер примет пакет от клиента, в поле ImmAddress блока ECB сервера окажется непосредственный адрес клиента. Поэтому сервер сможет ответить клиенту индивидуально, по его адресу в текущей сети.
Клиент, в свою очередь, получив пакет от сервера, сможет узнать его сетевой адрес (по содержимому поля ImmAddress блока ECB). Следующий пакет клиент отправит серверу используя полученный непосредственный адрес сервера.
Начиная с этого момента сервер знает адрес клиента, а клиент знает адрес сервера. Они могут обмениваться пакетами друг с другом не прибегая к посылке пакетов по адресу FFFFFFFFFFFFh.
Исходный текст программы-сервера приведен в листинге 4.
Вначале с помощью функции ipx_init() сервер проверяет наличие драйвера IPX и получает адрес его API. Затем с помощью функции IPXOpenSocket() программа открывает короткоживущий сокет с номером 0x4567. Этот номер мы выбрали произвольно из диапазона сокетов, распределяемых динамически.
Далее программа-сервер подготавливает блок ECB для приема пакета от клиента (RxECB). Сперва весь блок расписывается нулями. Затем заполняются поля номера сокета, счетчик фрагментов (всего используются два фрагмента) и дескрипторы фрагментов. Первый фрагмент предназначен для заголовка пакета, второй - для принятых данных.
Подготовленный блок ECB ставится в очередь на прием пакета при помощи функции IPXListenForPacket().
Затем программа в цикле опрашивает содержимое поля InUse блока ECB, дожидаясь прихода пакета. В цикл ожидания вставляется вызов функции IPXRelinquishControl() и функция опроса клавиатуры getch(). С помощью последней вы можете прервать ожидание, если нажмете на любую клавишу.
После того, как сервер примет пакет от клиента, содержимое поля данных (переданное клиентом в виде текстовой строки, закрытой двоичным нулем) будет выведено на консоль.
Приняв пакет, сервер подготавливает еще один блок ECB для передачи ответного пакета. Фактически сервер будет использовать тот же самый блок ECB, что и для приема. Поле непосредственного адреса в блоке ECB уже содержит адрес клиента, так как когда драйвер IPX принял пакет, он записал фактическое значение непосредственного адреса в соответствующее поле блока ECB. Для того, чтобы использовать блок ECB для передачи, нам достаточно изменить дескрипторы фрагментов - они должны указывать на заголовок передаваемого пакета и на буфер, содержащий передаваемые данные.
В качестве передаваемых данных сервер использует буфер TxBuffer с записанной в него текстовой строкой "SERVER *DEMO*". Эта строка будет выведена клиентом на консоль после приема от сервера ответного пакета.
Подготовив блок ECB для передачи, программа ставит его в очередь на передачу при помощи функции IPXSendPacket(), после чего закрывает сокет и завершает свою работу.
// Подготавливаем ECB для передачи пакета // Поле ImmAddress не заполняем, так как там // уже находится адрес станции клиента. // Это потому, что мы только что приняли от клиента пакет // данных и при этом в ECB установился непосредственный адрес // станции, которая отправила пакет
Программа-клиент (листинг 5) после проверки наличия драйвера IPX/SPX и получения адреса его API подготавливает блок ECB и передает первый пакет по адресу FFFFFFFFFFFFh. Его принимают все станции в текущей сети, но откликается на него только та станция, на которой запущена программа-сервер.
Послав первый пакет, клиент подготавливает ECB для приема пакета и ожидает ответ от сервера, вызывая в цикле функции IPXRelinquishControl и getch().
После прихода ответного пакета клиент закрывает сокет и завершает свою работу.
Исходные тексты функций для обращения к API драйвера IPX приведены в листинге 5.1. Здесь же определена функция IntSwap(), переставляющая местами байты в слове.
// =================================================== // Листинг 5.1. Функции IPX. // // Файл ipx.c // // (C) A. Frolov, 1993 // ===================================================
/** * .Name IntSwap * * .Title Обмен байтов в слове * * .Descr Функция меняет местами байты в слове, * которое передается ей в качестве параметра * * .Params unsigned i - преобразуемое слово * * .Return Преобразованное слово **/
unsigned IntSwap(unsigned i) { return((i>>8) | (i & 0xff)<<8); }
/** * .Name IPXOpenSocket * * .Title Открыть сокет * * .Descr Функция открывает сокет, тип которого * передается ей через параметр SocketType. * Перед вызовом необходимо подготовить в памяти * слово и записать в него значение открываемого * сокета (или нуль, если нужен динамический сокет). * Адрес слова передается через параметр Socket. * Если открывается динамический сокет, его * значение будет записано по адресу Socket. * * .Params int SocketType - тип сокета: * 0x00 - короткоживущий; * 0xFF - долгоживущий. * * unsigned *Socket - указатель на слово, * в котором находится номер * открываемого сокета или нуль, * если нужен динамический сокет. * * .Return 0 - сокет открыт успешно; * 0xFE - переполнилась таблица сокетов; * 0xFF - такой сокет уже открыт. **/
int IPXOpenSocket(int SocketType, unsigned *Socket) {
/** * .Name IPXCloseSocket * * .Title Закрыть сокет * * .Descr Функция закрывает сокет. * Перед вызовом необходимо подготовить в памяти * слово и записать в него значение закрываемого * сокета. Адрес слова передается через параметр Socket. * * .Params unsigned *Socket - указатель на слово, в котором * находится номер закрываемого сокета. * * .Return Ничего **/
/** * .Name IPXListenForPacket * * .Title Принять пакет * * .Descr Функция подготавливает ECB для приема * пакета из сети. Указатель на ECB передается * через параметр RxECB. * * .Params struct ECB *RxECB - указатель на ECB, * заполненное для приема пакета. * * .Return Ничего **/ void IPXListenForPacket(struct ECB *RxECB) {
/** * .Name IPXSendPacket * * .Title Передать пакет * .Descr Функция подготавливает ECB для передачи * пакета. Указатель на ECB передается через * параметр TxECB. * * .Params struct ECB *TxECB - указатель на ECB, * заполненное для передачи пакета. * * .Return Ничего **/
/** * .Name IPXRelinquishControl * * .Title Передать управление IPX при ожидании * * .Descr Функция используется при ожидании * завершения приема через опрос поля InUse блока ECB. * * .Params Не используются * * .Return Ничего **/
void IPXRelinquishControl(void) {
struct IPXSPX_REGS iregs;
iregs.bx = IPX_CMD_RELINQUISH_CONTROL; ipxspx_entry( (void far *)&iregs ); }
Листинг 6 содержит include-файл, в котором определены необходимые константы, структуры данных и прототипы функций.
// =================================================== // Листинг 6. Include-файл для работы с IPX // Файл ipx.h // // (C) A. Frolov, 1993 // ===================================================
// ----------------------- // Команды интерфейса IPX // -----------------------