Локальные сети персональных компьютеров Использование протоколов IPX, SPX, NETBIOS


Простая система "клиент-сервер" на базе SPX


Приведем простейший пример, демонстрирующий использование основных функций SPX. Этот пример сделан на базе предыдущего, в котором две програм-мы - клиент и сервер - общались между собой с помощью протокола IPX.

После определения наличия драйвера IPX и получения адреса его API программа-сервер с помощью функции SPXCheckSPXInstallation() определяет присутствие драйвера протокола SPX.

Затем открывается сокет для протокола IPX, подготавливается ECB для приема пакета от клиента. Этот пакет будет передаваться с помощью протокола IPX и предназначен для определения адреса клиента. Аналогично предыдущему примеру программа-клиент посылает пакет в текущую сеть с номером 00000000 по адресу FFFFFFFFFFFFh, т. е. всем станциям текущей сети. После того, как программа-сервер примет этот пакет, она сохранит в области памяти ClientImmAddress непосредственный адрес станции, на которой работает программа-клиент.

После этого программа-сервер, пользуясь полученным непосредственным адресом, посылает клиенту ответный IPX-пакет, сообщая о том, что сервер принял пакет от клиента.

Далее программа-сервер открывает еще один сокет, который будет использоваться протоколом SPX. Напомним, что для работы с протоколами IPX и SPX необходимо выделять разные сокеты.

Открыв сокет, сервер подготавливает блок ECB для приема SPX-пакета от клиента. В поле непосредственного адреса копируется непосредственный адрес клиента, полученный после приема от него IPX-пакета. Запрос на создание канала выдает функция SPXListenForSequencedPacket().

Далее программа-сервер подготавливает в структуре ConnECB блок ECB для создания канала и вызывает функцию создания канала с принимающей стороны SPXListenForConnection().

После создания канала программа-сервер ожидает прихода SPX-пакета, проверяя в цикле содержимое поля InUse блока LsECB, распределенного ранее функцией SPXListenForSequencedPacket() для приема SPX-пакетов.

После прихода SPX-пакета сервер закрывает оба сокета и завершает свою работу.

// =================================================== // Листинг 12.
Сервер SPX // // Файл spxserv.c // // (C) A. Frolov, 1993 // ===================================================

#include <stdio.h> #include <stdlib.h> #include <conio.h> #include <mem.h> #include <string.h> #include "ipx.h" #include "spx.h"



#define BUFFER_SIZE 512

void main(void) {

// Используем сокет 0x4568

static unsigned IPXSocket = 0x4567; static unsigned SPXSocket = 0x4568;

// Этот ECB используется для приема пакетов и для их передачи.

struct ECB RxECB; struct ECB ConnECB, LsECB;

// Заголовки принимаемых и передаваемых пакетов

struct IPX_HEADER RxHeader, TxHeader; struct SPX_HEADER ConnHeader, LsHeader;

// Буферы для принимаемых и передаваемых пакетов

unsigned char RxBuffer[BUFFER_SIZE]; unsigned char TxBuffer[BUFFER_SIZE];

struct SPXParams Params;

unsigned char ClientImmAddress[6];

printf("\n*Сервер SPX*, (C) Фролов А., 1993\n\n");

// Проверяем наличие драйвера IPX и определяем // адрес точки входа его API

if(ipx_init() != 0xff) { printf("IPX не загружен!\n"); exit(-1); }

if( SPXCheckSPXInstallation(&Params) != 0xFF) { printf("SPX не загружен!\n"); exit(-1); }

// Открываем сокет, на котором мы будем принимать пакеты

if(IPXOpenSocket(SHORT_LIVED, &IPXSocket)) { printf("Ошибка при открытии сокета IPX\n"); exit(-1); };

// Подготавливаем ECB для приема пакета

memset(&RxECB, 0, sizeof(RxECB)); RxECB.Socket = IntSwap(IPXSocket); 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;

IPXListenForPacket(&RxECB);

printf("Ожидание запроса от клиента\n"); printf("Для отмены нажмите любую клавишу\n");

while(RxECB.InUse) { IPXRelinquishControl(); if(kbhit()) { getch(); RxECB.CCode = 0xfe; break; } } if(RxECB.CCode == 0) { printf("Принят запрос от клиента '%s'\n", RxBuffer); printf("Для продолжения нажмите любую клавишу\n"); getch();



memcpy(ClientImmAddress, RxECB.ImmAddress,6);

// Подготавливаем ECB для передачи пакета // Поле ImmAddress не заполняем, так как там уже находится адрес // станции клиента. Это потому, что мы только что приняли от // клиента пакет данных и при этом в ECB установился непосред- // ственный адрес станции, которая отправила пакет

RxECB.Socket = IntSwap(IPXSocket); RxECB.FragmentCnt = 2; RxECB.Packet[0].Address = &TxHeader; RxECB.Packet[0].Size = sizeof(TxHeader); RxECB.Packet[1].Address = TxBuffer; RxECB.Packet[1].Size = BUFFER_SIZE;

// Подготавливаем заголовок пакета

TxHeader.PacketType = 4; memset(TxHeader.DestNetwork, 0, 4); memcpy(TxHeader.DestNode, RxECB.ImmAddress, 6); TxHeader.DestSocket = IntSwap(IPXSocket);

// Подготавливаем передаваемые данные

strcpy(TxBuffer, "SPX SERVER *DEMO*");

// Передаем пакет обратно клиенту

IPXSendPacket(&RxECB);

printf("Связь с сервером установлена\n"); printf("Создаем SPX-канал\n\n");

// Открываем сокет для работы с протоколом SPX

if(IPXOpenSocket(SHORT_LIVED, &SPXSocket)) { printf("Ошибка при открытии сокета SPX\n"); exit(-1); };

// Подготавливаем ECB для приема пакета

memset(&LsECB, 0, sizeof(LsECB)); LsECB.Socket = IntSwap(SPXSocket); memcpy(LsECB.ImmAddress, ClientImmAddress,6); LsECB.FragmentCnt = 2; LsECB.Packet[0].Address = &LsHeader; LsECB.Packet[0].Size = sizeof(LsHeader); LsECB.Packet[1].Address = RxBuffer; LsECB.Packet[1].Size = BUFFER_SIZE;

SPXListenForSequencedPacket(&LsECB);

// Подготавливаем заголовок пакета

ConnHeader.PacketType = 5; ConnHeader.TransportControl = 0; memset(ConnHeader.DestNetwork, 0, 4); memcpy(ConnHeader.DestNode, ClientImmAddress, 6); ConnHeader.DestSocket = IntSwap(SPXSocket); memset(&ConnECB, 0, sizeof(ConnECB)); ConnECB.Socket = IntSwap(SPXSocket); ConnECB.FragmentCnt = 1; ConnECB.Packet[0].Address = &ConnHeader; ConnECB.Packet[0].Size = sizeof(ConnHeader);

// Ожидаем запрос на создание канала

SPXListenForConnection(&ConnECB,0,0);



while(ConnECB.InUse) { IPXRelinquishControl(); if(kbhit()) { getch(); ConnECB.CCode = 0xfe; break; } }

if(ConnECB.CCode == 0) { printf("Канал %04.4X создан\n", (unsigned)ConnECB.ConnectionId); }

// Ожидаем прихода SPX-пакета от клиента

while(LsECB.InUse) { IPXRelinquishControl(); if(kbhit()) { getch(); LsECB.CCode = 0xfe; break; } } if(LsECB.CCode == 0) { printf("Пакет принят: '%s'\n", RxBuffer); } }

// Закрываем сокеты

IPXCloseSocket(&IPXSocket); IPXCloseSocket(&SPXSocket); exit(0); }

Программа- клиент после проверки наличия драйверов IPX и SPX открывает два сокета для использования с протоколами IPX и SPX. Затем подготавливается блок ECB для передачи "широковещательного" пакета по адресу FFFFFFFFFFFFh в текущую сеть с номером 000000. На этот пакет должна откликнуться программа-сервер, если она работает в текущей сети.

После передачи пакета программа-клиент ожидает прихода пакета от сервера. Затем она подготавливает блок ECB для приема SPX-пакета и ставит его в очередь на прием при помощи функции SPXListenForSequencedPacket().

Затем программа-клиент подготавливает блок ECB для создания канала с программой-сервером и вызывает функцию создания канала с передающей стороны SPXEstablishConnection().

После того, как канал будет создан, в область памяти ConnID копируется идентификатор канала для использования при приеме и передаче SPX-пакетов.

Далее программа-клиент подготавливает SPX-пакет и блок ECB для передачи программе-серверу и при помощи функции SPXSendSequencedPacket() передает пакет.

После передачи SPX-пакета программа-клиент закрывает оба сокета и завершает свою работу.

// =================================================== // Листинг 13. Клиент SPX // // Файл spxclien.c // // (C) A. Frolov, 1993 // ===================================================

#include <stdio.h> #include <stdlib.h> #include <conio.h> #include <mem.h> #include <string.h> #include "ipx.h" #include "spx.h"



// Максимальный размер буфера данных

#define BUFFER_SIZE 512

void main(void) {

// Будем работать с сокетом 0x4567

static unsigned IPXSocket = 0x4567; static unsigned SPXSocket = 0x4568;

// ECB для приема и передачи пакетов

struct ECB RxECB, TxECB; struct ECB ConnECB, LsECB, SndECB;

// Заголовки принимаемых и передаваемых пакетов

struct IPX_HEADER RxHeader, TxHeader; struct SPX_HEADER ConnHeader, LsHeader, SndHeader;

// Буферы для принимаемых и передаваемых данных

unsigned char RxBuffer[BUFFER_SIZE]; unsigned char TxBuffer[BUFFER_SIZE];

struct SPXParams Params;

unsigned char ServerImmAddress[6]; unsigned MyConnID, ConnID; unsigned rc;

printf("\n*Клиент SPX*, (C) Фролов А., 1993\n\n");

// Проверяем наличие драйвера IPX и определяем // адрес точки входа его API

if(ipx_init() != 0xff) { printf("IPX не загружен!\n"); exit(-1); }

if( SPXCheckSPXInstallation(&Params) != 0xFF) { printf("SPX не загружен!\n"); exit(-1); }

// Открываем сокет, на котором мы будем // принимать и передавать пакеты

if(IPXOpenSocket(SHORT_LIVED, &IPXSocket)) { printf("Ошибка при открытии сокета\n"); exit(-1); };

// Открываем сокет для протокола SPX

if(IPXOpenSocket(SHORT_LIVED, &SPXSocket)) { printf("Ошибка при открытии сокета SPX\n"); exit(-1); };

// Подготавливаем ECB для передачи пакета

memset(&TxECB, 0, sizeof(TxECB));

TxECB.Socket = IntSwap(IPXSocket); 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(IPXSocket);

// Записываем передаваемые данные

strcpy(TxBuffer, "CLIENT *DEMO*");

// Передаем пакет всем станциям в данной сети



IPXSendPacket(&TxECB);

// Подготавливаем ECB для приема пакета от сервера

memset(&RxECB, 0, sizeof(RxECB)); RxECB.Socket = IntSwap(IPXSocket); 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;

IPXListenForPacket(&RxECB);

printf("Ожидание ответа от сервера\n"); printf("Для отмены нажмите любую клавишу\n"); // Ожидаем прихода ответа от сервера while(RxECB.InUse) { IPXRelinquishControl(); if(kbhit()) { getch(); RxECB.CCode = 0xfe; break; } } if(RxECB.CCode == 0) { printf("Принят ответ от сервера '%s'\n", RxBuffer); }

// Копируем сетевой адрес сервера

memcpy(ServerImmAddress, RxECB.ImmAddress, 6);

// Подготавливаем ECB для приема пакета

memset(&LsECB, 0, sizeof(LsECB)); LsECB.Socket = IntSwap(SPXSocket); memcpy(LsECB.ImmAddress, ServerImmAddress,6); LsECB.FragmentCnt = 2; LsECB.Packet[0].Address = &LsHeader; LsECB.Packet[0].Size = sizeof(LsHeader); LsECB.Packet[1].Address = RxBuffer; LsECB.Packet[1].Size = BUFFER_SIZE;

SPXListenForSequencedPacket(&LsECB);

// Подготавливаем заголовок пакета

ConnHeader.PacketType = 5; ConnHeader.TransportControl = 0; memset(ConnHeader.DestNetwork, 0, 4); memcpy(ConnHeader.DestNode, ServerImmAddress, 6); ConnHeader.DestSocket = IntSwap(SPXSocket);

memset(&ConnECB, 0, sizeof(ConnECB)); ConnECB.Socket = IntSwap(SPXSocket); ConnECB.FragmentCnt = 1; ConnECB.Packet[0].Address = &ConnHeader; ConnECB.Packet[0].Size = sizeof(ConnHeader);

// Устанавливаем SPX-канал с сервером

rc = SPXEstablishConnection(&ConnECB, &MyConnID, 0, 0);

printf("Ожидание SPX-соединения с сервером\n"); printf("Для отмены нажмите любую клавишу\n");

if(rc == 0) { while(ConnECB.InUse) { IPXRelinquishControl(); if(kbhit()) { getch(); ConnECB.CCode = 0xfe; break; } } }

// Копируем идентификатор канала для передачи пакета в сервер

memcpy(&ConnID, &(ConnHeader.SourceConnID), 2);



printf("Канал с сервером установлен, ConnID=%d\n", IntSwap(ConnID));

// Подготавливаем ECB для передачи SPX-пакета

memset(&SndECB, 0, sizeof(SndECB));

SndECB.Socket = IntSwap(SPXSocket); SndECB.FragmentCnt = 2; SndECB.Packet[0].Address = &SndHeader; SndECB.Packet[0].Size = sizeof(SndHeader); SndECB.Packet[1].Address = TxBuffer; SndECB.Packet[1].Size = BUFFER_SIZE;

memcpy(SndECB.ImmAddress, ServerImmAddress, 6);

// Подготавливаем заголовок пакета SndHeader.PacketType = 5; memset(SndHeader.DestNetwork, 0, 4); memcpy(SndHeader.DestNode, ServerImmAddress, 6); SndHeader.DestSocket = IntSwap(SPXSocket); SndHeader.TransportControl = 0; SndHeader.DataStreamType = 1;

// Записываем передаваемые данные

strcpy(TxBuffer, "SPX/CLIENT *DEMO*");

// Передаем SPX-пакет

SPXSendSequencedPacket(&SndECB, ConnID);

// Закрываем сокеты

IPXCloseSocket(&IPXSocket); IPXCloseSocket(&SPXSocket);

exit(0); }

В файле spx. c определены функции для работы с протоколом SPX (листинг 14):

// =================================================== // Листинг 14. Функции SPX. // // Файл spx.c // // (C) A. Frolov, 1993 // ===================================================

#include <stdio.h> #include <stdlib.h> #include <dos.h> #include "ipx.h" #include "spx.h"

/** * .Name SPXCheckSPXInstallation * * .Title Проверить присутствие протокола SPX * * .Descr Функция проверяет, загружен ли драйвер SPX * и возвращает его параметры. * * .Params struct *SPXParams - указатель на структуру, * в которую будут записаны параметры SPX. * * .Return FFh - протокол SPX загружен * 00h - протокол SPX не загружен **/

int SPXCheckSPXInstallation(struct SPXParams *Params) {

struct IPXSPX_REGS iregs;

iregs.bx = SPX_CMD_INSTALL_CHECK; iregs.ax = 0; ipxspx_entry( (void far *)&iregs ); Params->SPXVersion = iregs.bx; Params->SPXMaxConnections = iregs.cx; Params->SPXAvailableConnCount = iregs.dx; return(iregs.ax & 0xFF); }

/** * .Name SPXListenForConnection * * .Title Ожидание соединения с клиентом * * .Descr Функция выдает запрос на соединение * с клиентом, который должен для выполнения * соединения вызвать функцию SPXEstablishConnection(). * * .Params struct ECB *ConnECB - указатель на ECB, * заполненное для установления соединения. * unsigned char RetryCount - счетчик повторов; * unsigned char WatchdogFlag - проверка связи. * * .Return Ничего. **/



void SPXListenForConnection(struct ECB *ConnECB, unsigned char RetryCount, unsigned char WatchdogFlag) {

struct IPXSPX_REGS iregs;

iregs.bx = SPX_CMD_LISTEN_FOR_CONNECTION; iregs.ax = RetryCount | ((unsigned)(WatchdogFlag << 8) & 0xff00); iregs.es = FP_SEG((void far*)ConnECB); iregs.si = FP_OFF((void far*)ConnECB);

ipxspx_entry( (void far *)&iregs ); }

/** * .Name SPXEstablishConnection * * .Title Установление соединения с клиентом * * .Descr Функция устанавливает соединение * с клиентом, который должен для выполнения * соединения вызвать функцию SPXListenForConnection(). * * .Params struct ECB *ConnECB - указатель на ECB, * заполненный для установления соединения. * unsigned char RetryCount - счетчик повторов; * unsigned char WatchdogFlag - проверка связи. * * .Return Ничего. **/

int SPXEstablishConnection(struct ECB *ConnECB, unsigned *ConnID, unsigned char RetryCount, unsigned char WatchdogFlag) {

struct IPXSPX_REGS iregs;

iregs.bx = SPX_CMD_ESTABLISH_CONNECTION; iregs.ax = RetryCount | ((unsigned)(WatchdogFlag << 8) & 0xff00); iregs.es = FP_SEG((void far*)ConnECB); iregs.si = FP_OFF((void far*)ConnECB);

ipxspx_entry( (void far *)&iregs ); *ConnID = iregs.dx; return(iregs.ax & 0xff); }

/** * .Name SPXListenForSequencedPacket * * .Title Прием пакета SPX * * .Descr Функция выдает запрос на прием пакета SPX. * * .Params struct ECB *LsECB - указатель на ECB, * заполненный для приема SPX-пакета. * * .Return Ничего. **/

void SPXListenForSequencedPacket(struct ECB *LsECB) {

struct IPXSPX_REGS iregs;

iregs.bx = SPX_CMD_LISTEN_FOR_SEQUENCED_PACKET; iregs.es = FP_SEG((void far*)LsECB); iregs.si = FP_OFF((void far*)LsECB);

ipxspx_entry( (void far *)&iregs ); }

/** * .Name SPXSendSequencedPacket * * .Title Передача пакета SPX * * .Descr Функция выдает запрос на передачу пакета SPX. * * .Params struct ECB *TxECB - указатель на ECB, * заполненный для передачи SPX-пакета. * * .Return Ничего. **/

void SPXSendSequencedPacket(struct ECB *TxECB,unsigned ConnID) {



struct IPXSPX_REGS iregs;

iregs.bx = SPX_CMD_SEND_SEQUENCED_PACKET; iregs.es = FP_SEG((void far*)TxECB); iregs.si = FP_OFF((void far*)TxECB); iregs.dx = ConnID;

ipxspx_entry( (void far *)&iregs ); }

В файле spx.h ( листинг 15) определены константы, структуры данных и прототипы функций для работы с протоколом SPX:

// =================================================== // Листинг 15. Include-файл для работы с SPX // Файл spx.h // // (C) A. Frolov, 1992 // ===================================================

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

// ----------------------- // Команды интерфейса SPX // -----------------------

#define SPX_CMD_INSTALL_CHECK 0x10 #define SPX_CMD_ESTABLISH_CONNECTION 0x11 #define SPX_CMD_LISTEN_FOR_CONNECTION 0x12 #define SPX_CMD_TERMINATE_CONNECTION 0x13 #define SPX_CMD_ABORT_CONNECTION 0x14 #define SPX_CMD_GET_CONNECTION_STATUS 0x15 #define SPX_CMD_SEND_SEQUENCED_PACKET 0x16 #define SPX_CMD_LISTEN_FOR_SEQUENCED_PACKET 0x17

struct SPXParams { unsigned SPXVersion; unsigned SPXMaxConnections; unsigned SPXAvailableConnCount; };

// ========================================================= // Заголовок пакета SPX // =========================================================

struct SPX_HEADER { unsigned int Checksum; unsigned int Length; unsigned char TransportControl; unsigned char PacketType; unsigned char DestNetwork[4]; unsigned char DestNode[6]; unsigned int DestSocket; unsigned char SourceNetwork[4]; unsigned char SourceNode[6]; unsigned int SourceSocket; // ------------Специфическая для SPX часть --------- unsigned char ConnControl; unsigned char DataStreamType; unsigned char SourceConnID[2]; unsigned char DestConnID[2]; unsigned char SequenceNumber[2]; unsigned char AckNumber[2]; unsigned char AllocationNumber[2]; };

int SPXCheckSPXInstallation(struct SPXParams *Params); void SPXListenForConnection(struct ECB *ConnECB, unsigned char RetryCount, unsigned char WatchdogFlag); int SPXEstablishConnection(struct ECB *ConnECB, unsigned *ConnID, unsigned char RetryCount, unsigned char WatchdogFlag); void SPXListenForSequencedPacket(struct ECB *LsECB); void SPXSendSequencedPacket(struct ECB *TxECB, unsigned MyConnID);


Содержание раздела