Электронная библиотека книг Александра Фролова и Григория Фролова.
Shop2You.ru Создайте свой интернет-магазин
Библиотека
Братьев
Фроловых
Локальные сети персональных компьютеров. Использование протоколов IPX, SPX, NETBIOS
© Александр Фролов, Григорий Фролов
Том 4, М.: Диалог-МИФИ, 1993, 160 стр.

[Назад] [Содеожание] [Дальше]

3.4. Простая система "клиент-сервер" на базе 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);



[Назад] [Содеожание] [Дальше]