Локальные сети персональных компьютеров. Использование протоколов 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, ®s, ®s, &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();
}
};
|

