Электронная библиотека книг Александра Фролова и Григория Фролова.
 
Библиотека
Братьев
Фроловых
Электронная библиотека книг Александра Фролова и Григория Фролова.
Библиотека системного программиста
Программирование на JAVA
ПК. Шаг за шагом
Другие книги
Восстановление данных
Антивирусная защита
Статьи для
программистов
Пользователю компьютера

Локальные сети персональных компьютеров. Использование протоколов 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();
        }
};
[Назад] [Содеожание] [Дальше]


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