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

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

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

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

4.5. Система "клиент-сервер" на базе датаграмм

Приведем пример простейшей системы "клиент-сервер", в которую входят две программы, обменивающиеся датаграммами (листинг 18).

Аналогичная система, сделанная на базе протокола IPX, требовала использования процедуры определения сетевых адресов сервера и клиента. Так как сервер может быть запущен на любой станции в сети, программа-клиент сразу после своего запуска не может знать сетевой адрес сервера. Если вы помните, для определения адреса программы-сервера программа-клиент посылала пакет в "широковещательном" режиме сразу всем станциям в сети. Сервер, приняв этот пакет, отвечал клиенту, и таким образом программы узнавали адрес друг друга. Единственное, что должно быть известно программе-клиенту, - это номер сокета, на котором программа-сервер ожидает прихода пакетов.

При использовании протокола NETBIOS ситуация сильно упрощается. Вполне достаточно, если программа-клиент будет знать имя, которое добавляется сервером и используется для приема пакетов от клиентов.

Программа-сервер начинает свою работу с создания объекта класса NETBIOS_DATAGRAM_SERVER. Конструктор в качестве параметра получает имя, добавляемое сервером и используемое для приема пакетов. В нашем случае это имя "NETBIOS Server".

В процессе своей работы конструктор класса NETBIOS_DATAGRAM_SERVER проверяет наличие интерфейса NETBIOS, добавляет имя, полученное им в качестве параметра, и сохраняет полученный номер имени для дальнейшего использования. После завершения работы программы деструктор класса NETBIOS_DATAGRAM_SERVER удалит добавленное имя.

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

// ===================================================
// Листинг 18. Сервер 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_DATAGRAM_SERVER {

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

public:

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

        char OurName[16];
        unsigned NetworkNameNumber;

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

        NETBIOS_DATAGRAM_SERVER(char *Name) {

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

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

                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();
        }

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

        ~NETBIOS_DATAGRAM_SERVER() {
                NCB AddNameNCB;
                AddNameNCB.WDeleteName(OurName);
                errno = AddNameNCB.Error();
        }

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

        int Error(void) {return errno;}

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

        void Receive(char *ReceiveBuffer, unsigned BufferSize) {
                NCB ReceiveNCB;

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

                ReceiveNCB.SetBuffer(ReceiveBuffer, BufferSize);

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

                ReceiveNCB.WReceiveDatagram(NetworkNameNumber);

        }
};

void main(void) {

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

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

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

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

// Принимаем сообщение от клиента

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

}

Программа-сервер работает в паре с программой-клиентом (листинг 19).

После запуска программа-клиент создает объект класса NETBIOS_DATAGRAM_CLIENT. Конструктор и деструктор этого класса выполняют действия, аналогичные конструктору и деструктору класса NETBIOS_DATAGRAM_SERVER.

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

// ===================================================
// Листинг 19. Клиент 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_DATAGRAM_CLIENT {

        unsigned errno;

public:

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

        char OurName[16];
        unsigned NetworkNameNumber;
        union REGS regs;

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

        NETBIOS_DATAGRAM_CLIENT(char *Name) {

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

                NCB AddNameNCB;

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

                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();
        }

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

        ~NETBIOS_DATAGRAM_CLIENT() {
                NCB AddNameNCB;
                AddNameNCB.WDeleteName(OurName);
                errno = AddNameNCB.Error();
        }

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

        int Error(void) {return errno;}

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

        void Receive(char *ReceiveBuffer, unsigned BufferSize) {
                NCB ReceiveNCB;

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

                ReceiveNCB.SetBuffer(ReceiveBuffer, BufferSize);

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

                ReceiveNCB.WReceiveDatagram(NetworkNameNumber);

        }

// Функция для передачи датаграммы

        void Send(char *ReceiveBuffer, unsigned BufferSize,
                                        char *CallName) {
                NCB SendNCB;

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

                SendNCB.SetBuffer(ReceiveBuffer, BufferSize);

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

                SendNCB.SetCallName(CallName);

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

                SendNCB.WSendDatagram(NetworkNameNumber);
        }
};

void main(void) {

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

        NETBIOS_DATAGRAM_CLIENT Client("NETBIOS Client");

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

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

// Передаем сообщение серверу с именем "NETBIOS Server"

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

}

В include-файле netbios.hpp (листинг 20) приведены определения констант и классов для работы с протоколом NETBIOS через прерывание INT 5Ch.

В классе NCB, кроме структуры данных _NCB, определены конструктор NCB() и несколько других функций для работы с этим классом.

Конструктор расписывает структуру ncb нулями и сбрасывает код ошибки в переменной errno.

Функция NetBios() вызывает прерывание NETBIOS.

Функции WAddName() и WDeleteName() определены в файле nbfunc.cpp (листинг 21). Они предназначены для добавления и удаления имени.

Назначение остальных функций вы можете узнать, прочитав комментарии к программе в листинге 20.

// ===================================================
// Листинг 20. Классы для работы с 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 SetCallName(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();
        }

};

В файле nbfunc.cpp приведены определения некоторых функций из класса NCB (листинг 21):

// ===================================================
// Листинг 21. Функции для NETBIOS
//
// Файл nbfunc.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"

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

        void NCB::WAddName(char *name) {
                char buf[16];

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

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

// При необходимости дополняем имя пробелами

                while (strlen(buf) < 15) strcat(buf, " ");

// Вызываем NETBIOS

                ncb.Cmd = NB_WAddName;
                strcpy(ncb.OurName, buf);
                NetBios();
                errno = ncb.FinalCCode;
        }

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

        void NCB::WDeleteName(char *name) {
                char buf[16];

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

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

// При необходимости дополняем имя пробелами

                while (strlen(buf) < 15) strcat(buf, " ");

                strcpy(ncb.OurName, buf);

// Вызываем NETBIOS

                ncb.Cmd = NB_WDeleteName;
                NetBios();
                errno = ncb.FinalCCode;
        }

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

        void NCB::SetCallName(char *name) {
                char buf[16];
                if(strlen(name) > 15) {
                        errno = 0xff;
                        return;
                }
                strcpy(buf, name);
                while (strlen(buf) < 15) strcat(buf, " ");
                strcpy(ncb.CallName, buf);
        }

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


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