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

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


Приведем пример системы "клиент-сервер", реализованной с использованием каналов протокола 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(); } };


Содержание раздела