Электронная библиотека книг Александра Фролова и Григория Фролова.
Shop2You.ru Создайте свой интернет-магазин
Библиотека
Братьев
Фроловых

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

© Александр Фролов, Григорий Фролов
Том 4, М.: Диалог-МИФИ, 1993, 160 стр.

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

4.6. Система "клиент-сервер" на базе каналов

Приведем пример системы "клиент-сервер", реализованной с использованием каналов протокола NETBIOS (листинг 22).

После запуска программа-сервер создает объект класса NETBIOS_SESSION_SERVER. Конструктор этого объекта проверяет присутствие интерфейса NETBIOS, добавляет имя, переданное ему в качестве параметра, затем создает канал при помощи функции WListen().

Деструктор класса NETBIOS_SESSION_SERVER перед удалением имени удаляет канал, так как имя нельзя удалить, если оно используется каким-либо каналом.

После того, как отработал конструктор, программа-сервер проверяет ошибки и вызывает функцию Receive(), которая ожидает приема данных по созданному каналу. После приема сервер отображает принятые данные как текстовую строку и завершает свою работу.

// ===================================================
// Листинг 22. Сервер NETBIOS, вариант с
//             использованием каналов
//
// Файл nbserver.cpp
//
// (C) A. Frolov, 1993
// ===================================================

#include <stdio.h>
#include <stdlib.h>
#include <dos.h>
#include <conio.h>
#include <mem.h>
#include <string.h>
#include "netbios.hpp"

// Класс серверов NETBIOS

class NETBIOS_SESSION_SERVER {

        unsigned errno;
        void interrupt ( *int5C)(...);

public:

// Здесь хранится имя сервера и номер этого имени

        char OurName[16];
        unsigned NetworkNameNumber;

// Блок NCB, который будет использован при добавлении имени

        NCB AddNameNCB;

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

        NETBIOS_SESSION_SERVER(char *Name) {

// Проверяем длину имени имя

                if(strlen(Name) > 15) {
                        errno = 0xff;
                        return;
                }
                strcpy(OurName, Name);

// Проверяем наличие интерфейса NETBIOS

                int5C = getvect(0x5c);
                errno = 0;
                if(FP_SEG(int5C) == 0x0000 || FP_SEG(int5C) == 0xF000) {
                        errno=0xff;
                        exit(-1);
                }

// Добавляем имя

                AddNameNCB.WAddName(OurName);

// Запоминаем полученный номер имени

                NetworkNameNumber = AddNameNCB.GetNetworkNameNumber();
                errno = AddNameNCB.Error();
                if(errno) return;

// Устанавливаем "*" в поле CallName, это означает,
// что сервер будет обрабатывать запросы на создание
// канала от любого имени

                AddNameNCB.SetCallName("*");

// Устанавливаем время тайм-аута для команд
// приема и передачи данных по каналу

                AddNameNCB.SetRtoSto(20,20);

// Создаем канал с принимающей стороны

                AddNameNCB.WListen();
        }

// Деструктор, удаляет канал и имя.

        ~NETBIOS_SESSION_SERVER() {

// Удаление канала

                AddNameNCB.WHangUp();

// Удаление имени

                AddNameNCB.WDeleteName(OurName);
                errno = AddNameNCB.Error();
        }

// Функция для проверки кода ошибки

        int Error(void) {return errno;}

// Функция для приема данных по каналу

        void Receive(char *ReceiveBuffer, unsigned BufferSize) {

// Записываем в NCB адрес и длину буфера

                AddNameNCB.SetBuffer(ReceiveBuffer, BufferSize);

// Выполняем прием датаграммы с ожиданием

                AddNameNCB.WReceive();

        }
};

void main(void) {

// Наш сервер с именем "NETBIOS Server"

        NETBIOS_SESSION_SERVER Server("NETBIOS Server");
        char ReceiveBuffer[512];

// Проверяем, были ли ошибки на этапе инициализации сервера.

        if(Server.Error()) {
                printf("Ошибка %02.2X\n", Server.Error());
                return;
        }
        printf("Инициализация завершена.\n");
        printf("Ожидаем сообщение от клиента.\n");

// Принимаем сообщение от клиента по каналу, который был создан 
// конструктором класса NETBIOS_SESSION_SERVER

        Server.Receive(ReceiveBuffer, 512);
        printf("Принято: >%s<\n",ReceiveBuffer);
}

В файле nbclient.cpp находится программа-клиент (листинг 23), работающая в паре с только что приведенной программой-сервером.

Программа-клиент создает объект NETBIOS_SESSION_CLIENT, конструктор которого выполняет действия, аналогичные конструктору класса NETBIOS_SESSION_SERVER. Есть одно отличие: для создания канала в конструкторе класса NETBIOS_SESSION_CLIENT используется функция WCall(), а не WListen(). Конструктор создает канал с программой-сервером, указывая имя "NETBIOS Server", которое используется сервером для работы с клиентом.

После проверки ошибок программа-клиент с помощью функции Send() передает по созданному каналу программе-серверу сообщение "Привет от клиента NETBIOS!" и завершает свою работу.

// ===================================================
// Листинг 23. Клиент NETBIOS, вариант с
//             использованием каналов
//
// Файл nbclient.cpp
//
// (C) A. Frolov, 1993
// ===================================================

#include <stdio.h>
#include <stdlib.h>
#include <dos.h>
#include <conio.h>
#include <mem.h>
#include <string.h>
#include "netbios.hpp"

// Класс клиентов NETBIOS

class NETBIOS_SESSION_CLIENT {

        unsigned errno;
        void interrupt ( *int5C)(...);

// Блок NCB, который будет использован при добавлении имени

                NCB AddNameNCB;

public:

// Здесь хранится имя клиента и номер этого имени

        char OurName[16];
        unsigned NetworkNameNumber;

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

        NETBIOS_SESSION_CLIENT(char *Name) {

// Проверяем длину имени имя

                if(strlen(Name) > 15) {
                        errno = 0xff;
                        return;
                }
                strcpy(OurName, Name);

// Проверяем наличие интерфейса NETBIOS

                int5C = getvect(0x5c);
                errno = 0;
                if(FP_SEG(int5C) == 0x0000 || FP_SEG(int5C) == 0xF000) {
                        errno=0xff;
                        exit(-1);
                }

// Добавляем имя

                AddNameNCB.WAddName(OurName);

// Запоминаем полученный номер имени

                NetworkNameNumber = AddNameNCB.GetNetworkNameNumber();

// Если при добавлении имени были ошибки,
// завершаем работу программы

                errno = AddNameNCB.Error();
                if(errno) return;

// Устанавливаем имя сервера, с которым будем создавать канал

                AddNameNCB.SetCallName("NETBIOS Server");

// Устанавливаем время тайм-аута
// для передачи и приема данных по каналу

                AddNameNCB.SetRtoSto(20,20);

// Устанавливаем канал с передающей стороны

                AddNameNCB.WCall();
        }

// Деструктор, удаляет канал и имя.

        ~NETBIOS_SESSION_CLIENT() {

// Удаление канала

                AddNameNCB.WHangUp();

// Удаление имени

                AddNameNCB.WDeleteName(OurName);
                errno = AddNameNCB.Error();
        }
// Функция для проверки кода ошибки

        int Error(void) {return errno;}

// Функция для передачи по каналу

        void Send(char *ReceiveBuffer, unsigned BufferSize) {

// Устанавливаем адрес и длину буфера

                AddNameNCB.SetBuffer(ReceiveBuffer, BufferSize);

// Передаем данные по каналу с ожиданием

                AddNameNCB.WSend();
        }
};

void main(void) {

// Наш клиент с именем "NETBIOS Client"

        NETBIOS_SESSION_CLIENT Client("NETBIOS Client");

// Проверяем, были ли ошибки на этапе инициализации клиента.

        if(Client.Error()) {
                printf("Ошибка %02.2X\n", Client.Error());
                return;
        }
        printf("Инициализация завершена.\n");

// Передаем сообщение серверу по созданному каналу. Канал был 
// создан при работе конструктора класса NETBIOS_SESSION_CLIENT.

        Client.Send("Привет от клиента NETBIOS!", 512);
}

Файл netbios.hpp (листинг 24) содержит все необходимые определения для программ, работающих с каналами NETBIOS:

// ===================================================
// Листинг 24. Классы для работы с NETBIOS
//
// Файл netbios.hpp
//
// (C) A. Frolov, 1993
// ===================================================

// Команды NETBIOS

// Команды для работы с именами

#define NB_WAddName 0x30
#define NB_AddName 0xb0
#define NB_WAddGroupName 0x36
#define NB_AddGroupName 0xb6
#define NB_WDeleteName 0x31
#define NB_DeleteName 0xb1

// Команды для передачи датаграмм

#define NB_WSendDatagram 0x20
#define NB_SendDatagram 0xa0
#define NB_WSendBroadcastDatagram 0x22
#define NB_SendBroadcastDatagram 0xa2

// Команды для приема датаграмм

#define NB_WReceiveDatagram 0x21
#define NB_ReceiveDatagram 0xa1
#define NB_WReceiveBroadcastDatagram 0x23
#define NB_ReceiveBroadcastDatagram 0xa3

// Команды для работы с каналами

#define NB_WCall 0x10
#define NB_Call 0x90
#define NB_WListen 0x11
#define NB_Listen 0x91
#define NB_WHangUp 0x12
#define NB_HangUp 0x92

// Команды для передачи данных по каналу

#define NB_WSend 0x14
#define NB_Send 0x94
#define NB_WSendNoAck 0x71
#define NB_SendNoAck 0xf1

#define NB_WChainSend 0x17
#define NB_ChainSend 0x97
#define NB_WChainSendNoAck 0x72
#define NB_ChainSendNoAck 0xf2

// Команды для приема данных по каналу

#define NB_WReceive 0x15
#define NB_Receive 0x95
#define NB_WReceiveAny 0x16
#define NB_ReceiveAny 0x96

// Прочие команды

#define NB_WResetAdapter 0x32
#define NB_WCancel 0x35
#define NB_WSessionStatus 0x34
#define NB_SessionStatus 0xb4


// Класс NCB для работы с командами NETBIOS

class NCB {

// Стандартный блок NCB в формате NETBIOS

        struct _NCB {
                unsigned char Cmd;
                unsigned char CCode;
                unsigned char LocalSessionNumber;
                unsigned char NetworkNameNumber;
                void far *Buffer;
                unsigned Size;
                char CallName[16];
                char OurName[16];
                unsigned char ReceiveTimeout;
                unsigned char SendTimeout;
                void interrupt (*PostRoutine)(void);
                unsigned char AdapterNumber;
                unsigned char FinalCCode;
                unsigned char Reserved[14];
        } ncb;
        struct SREGS sregs;
        union REGS regs;
        unsigned errno;
// Функция для вызова NETBIOS

        void NetBios(void) {
                sregs.es = FP_SEG(&ncb);
                regs.x.bx = FP_OFF(&ncb);
                int86x(0x5c, &regs, &regs, &sregs);
        }

public:

// Конструктор, расписывает ncb нулями

        NCB() {
                memset(&ncb, 0, sizeof(ncb));
                errno = 0;
        }

// Функция возвращает код ошибки

        int Error(void) {return errno;}

// Функция для добавления имени

        void WAddName(char *name);

// Функция для удаления имени

        void WDeleteName(char *name);

// Функция для определения номера имени

        unsigned GetNetworkNameNumber(void)
                { return(ncb.NetworkNameNumber); }

// Функция для установки адреса и размера буфера

        void SetBuffer(char far *Buf,   unsigned BufSize) {
                ncb.Buffer = Buf;
                ncb.Size = BufSize;
        }

// Установка в NCB тайм-аута

        void SetRtoSto(int rto, int sto) {
                ncb.ReceiveTimeout = rto;
                ncb.SendTimeout = sto;
        }

// Установка в ncb имени вызываемого партнера

        void SetCallName(char *name);

// Установка в ncb имени нашей станции

        void SetOurName(char *name);

// Прием датаграмм с ожиданием

        void WReceiveDatagram(int NetwrkNameNumber) {

// Заполняем поле номера своего имени

                ncb.NetworkNameNumber = NetwrkNameNumber;

// Вызываем NETBIOS

                ncb.Cmd = NB_WReceiveDatagram;
                NetBios();
        }

// Передача датаграмм с ожиданием

        void WSendDatagram(int NetwrkNameNumber) {

// Заполняем поле номера своего имени

                ncb.NetworkNameNumber = NetwrkNameNumber;
                ncb.Cmd = NB_WSendDatagram;

// Вызываем NETBIOS

                NetBios();
        }

// Создание канала с принимающей стороны

        void WListen(void) {
                ncb.Cmd = NB_WListen;
                NetBios();
        }

// Создание канала с передающей стороны

        void WCall(void) {
                ncb.Cmd = NB_WCall;
                NetBios();
        }

// Удаление канала

        void WHangUp(void) {
                ncb.Cmd = NB_WHangUp;
                NetBios();
        }

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

        void WReceive(void) {
                ncb.Cmd = NB_WReceive;
                NetBios();
        }

// Передача в канал с ожиданием

        void WSend(void) {
                ncb.Cmd = NB_WSend;
                NetBios();
        }
};
[Назад] [Содеожание] [Дальше]