Электронная библиотека книг Александра Фролова и Григория Фролова.
 
Библиотека
Братьев
Фроловых
Электронная библиотека книг Александра Фролова и Григория Фролова.
Библиотека системного программиста
Программирование на JAVA
ПК. Шаг за шагом
Другие книги
Восстановление данных
Антивирусная защита
Статьи для
программистов
Пользователю компьютера
Локальные сети персональных компьютеров. Использование протоколов 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);



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


Создание интернет-магазинов: http://www.shop2you.ru/ © Александр Фролов, Григорий Фролов, 1991-2016