Локальные сети персональных компьютеров. Работа с сервером Novell NetWare

Программа SLIST


Мы подготовили для вас программу, которая, пользуясь протоколом SAP, определяет список активных серверов и запоминает имена серверов. Затем для всех активных серверов программа получает дополнительную информацию и выводит ее в стандартный поток вывода.

Программа создает объект класса SLIST. Конструктор этого объекта получает всю необходимую информацию, которая при помощи функции SLIST::PrintServersName(), определенной в классе SLIST, выводится в стандартный поток (листинг 3).

// ================================================================ // Листинг 3. Просмотр списка активных серверов и вывод в стандарт- // ный поток имен и другой информации об активных серверах // Файл slist!\slist.cpp // // (C) A. Frolov, 1993 // ================================================================

#include <stdlib.h> #include <stdio.h> #include <mem.h> #include <string.h> #include <dos.h> #include <direct.h> #include "sap.hpp"

void main(void) {

SLIST *ServerList; int ccode = 0;

printf("\n*SLIST!*, v.1.0, (C) Фролов А.В., 1993\n");

// Создаем объект класса SLIST. Конструктор этого объекта // получает всю необходимую информацию о серверах и // записывает ее в область данных объекта

ServerList = new SLIST(GENERAL_SERVICE);

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

ccode = ServerList->Error();

if(ccode) { printf("Ошибка %d\n", ccode); return; }

// Выводим список серверов



printf("\nОбнаружены серверы:\n");

printf( "---------------------------------------------" "------------------------------\n");

ServerList->PrintServersName();

printf( "---------------------------------------------" "------------------------------\n"); }

Файл slist.cpp содержит определения функций-членов класса SLIST (листинг 4).

Конструктор SLIST() проверяет наличие сетевой оболочки, проверяет и запоминает тип запроса (получить сведения о ближайшем сервере или о всех серверах сети) и запоминает его.
Затем конструктор инициализирует драйвер протокола IPX и открывает динамический короткоживущий сокет для работы с протоколом SAP. Далее в цикле создаются блоки ECB и ставятся в очередь на прием пакетов. Эти блоки ECB будут использованы для приема SAP-пакетов. После подготовки ECB конструктор посылает пакет запроса, ожидает примерно одну секунду и при помощи функций SLIST::GetServersName() и SLIST::GetServersInfo() получает и запоминает имена серверов и другую информацию.

Для работы с IPX-пакетами мы использовали функции из библиотеки NetWare C Interface. Назначение этих функций вам будет понятно из их названия, если вы прочитали предыдущий том "Библиотеки системного программиста".

Функция IPXInitialize() проверяет наличие драйвера протокола IPX и выполняет все инициализирующие действия, необходимые для использования протокола IPX.

Функция IPXOpenSocket() предназначена для открытия сокета. Первый параметр функции - указатель на переменную типа WORD, содержащую значение открываемого сокета или ноль, если надо получить динамический сокет. Байты в этой переменной расположены в обратном порядке, т. е. старший байт расположен по младшему адресу. Второй параметр функции IPXOpenSocket() определяет тип открываемого сокета - долгоживущий или короткоживущий. В нашем случае мы открываем динамический короткоживущий сокет.

После открытия сокета конструктор с помощью функции SLIST::ReceiveSAPPacket() подготавливает массив блоков ECB для приема ответных пакетов и, вызывая функцию IPXListenForPacket(), ставит эти блоки в очередь на прием. Функция IPXListenForPacket() имеет в качестве единственного параметра указатель на блок ECB.

Далее конструктор вызывает функцию SLIST::SendSAPPacket(), которая подготавливает блок ECB и заголовок IPX-пакета для SAP-запроса. При этом с помощью функции IPXGetInternetworkAddress() программа определяет свой собственный сетевой адрес. Функция IPXGetInternetworkAddress() имеет в качестве параметра указатель на структуру, в которую будет записан номер сети и сетевой адрес узла в сети.



Подготовив пакет, функция SLIST::SendSAPPacket() ставит его в очередь на передачу при помощи функции IPXSendPacket(), передавая ей в качестве параметра указатель на соответствующий блок ECB.

Когда пакет будет передан, конструктор ждет примерно одну секунду. В течение этого времени приходят ответные пакеты от серверов. После ожидания вызываются функции SLIST::GetServersName() и SLIST::GetServersInfo(), получающие соответсвенно имена серверов и дополнительную информацию.

Функция SLIST::GetServersName() переписывает имена откликнувшихся на запрос серверов из принятых SAP-пакетов во внутренний массив объекта класса SLIST.

Функция SLIST::GetServersInfo() выполняет более сложные действия.

Вначале с помощью функций GetPrimaryConnectionID() и GetDefaultConnectionID() она получает номера каналов первичного и текущего серверов, записывая их во внутренние переменные объекта класса SLIST. Затем запускается цикл по всем обнаруженным в сети серверам.

Внутри этого цикла для каждого сервера функция получает его номер канала при помощи функции GetConnectionID(). Если канала нет, рабочая станция создает его, подключаясь к серверу. Для подключения используется функция AttachToFileServer().

Затем сервер делается предпочтительным, для чего вызывается функция SetPreferredConnectionID(). Теперь все запросы будут идти к предпочтительному серверу. Внутри цикла мы по очереди будем делать все имеющиеся серверы предпочтительными и, направляя запросы, получать от серверов интересующую нас информацию.

Далее функция SLIST::GetServersInfo() вызывает функцию GetServerInformation(), которая записывает сведения о сервере в структуру ServerInfo. Первый параметр функции GetServerInformation() задает размер этой структуры, а второй является указателем на нее.

Перед возвратом функция SLIST::GetServersInfo() пытается получить серийный номер операционной системы Novell NetWare, работающей на предпочтительном файл-сервере, вызывая функцию GetNetworkSerialNumber(). Этой функции в качестве первого параметра необходимо передать указатель на переменную типа long, в качестве второго - указатель на переменную типа WORD.


В первую переменную функция запишет серийный номер операционной системы, во вторую - серийный номер приложения, работающего на файл-сервере.

Надо заметить, что данная функция возвращает серийный номер только для тех серверов, к которым было выполнено подключение пользователя функцией LoginToFileServer(). Поэтому перед вызовом функции GetNetworkSerialNumber() мы записываем в поле серийного номера и номера приложения нулевое значение. Если содержимое этих полей останется нулевым, значит, пользователь не подключился к данному файл-серверу. Для сокращения размера листинга мы не проверяем код ошибки, возвращаемый функцией GetNetworkSerialNumber().

Функция SLIST::PrintServersName() в цикле для всех обнаруженных серверов выводит в стандартный поток вывода имя сервера, напротив которого указывается, является ли он первичным (Primary) или текущим (Default). Затем выводится версия Novell NetWare, взятая из полей netwareVersion и netwareSubVersion структуры ServerInfo. Для подключенных серверов выводится серийный номер и номер приложения.

Далее для всех серверов выводится номер канала, используемого сервером и записанного ранее в массив ConnID[].

После этого для каждого сервера выводится содержимое полей maxConnectionsSupported и connectionsInUse структуры ServerInfo, которые содержат максимальное количество каналов для сервера и количество каналов, используемых в данный момент.

Перед окончанием работы программы вызывается деструктор, который отменяет все ожидающие приема блоки ECB и закрывает динамический сокет. Для отмены блоков ECB используется функция IPXCancelEvent(), которой в качестве параметра передается указатель на отменяемый блок ECB. Сокет закрывается при помощи функции IPXCloseSocket(). Номер закрываемого сокета передается этой функции в качестве параметра.

// =================================================== // Листинг 4. Функции для программы SLIST.CPP // Файл slist!\sap.cpp // // (C) A. Frolov, 1993 // ===================================================



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

// ==================================================== // Конструктор класса SLIST // ====================================================

SLIST::SLIST(int ServiceType) {

// Проверяем наличие сетевой оболочки и определяем ее версию

MajorVersion = 0;

asm push si GetNetWareShellVersion(&MajorVersion, &MinorVersion, &Revision); asm pop si

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

if(MajorVersion == 0) { printf("\nОболочка NetWare не загружена\n"); errno = 0xff; return; }

// Проверяем тип SAP-запроса

if (ServiceType != 1 && ServiceType != 3) { errno = NOT_SUPPORTED; return; } // Запоминаем тип запроса

QueryType = ServiceType;

// Инициализируем драйвер протокола IPX

IPXInitialize();

// Открываем короткоживущий динамический сокет

SrcSocket = 0x00; errno = IPXOpenSocket(&SrcSocket, SHORT_LIVED);

// Заполняем таблицу имен серверов нулями

memset(ServerName,0,sizeof(ServerName));

// Подготавливаем блоки ECB для приема // пакетов от SAP-протокола

for(int i=0;i<MAX_SERVERS;i++) {

// Заполняем блок ECB

ReceiveSAPPacket(&Query[i]);

// Ставим в очередь на прием пакета

IPXListenForPacket(&Query[i].theECB); }

// Если не было ошибок, посылаем запрос

if (!errno) { SendSAPPacket();

// Ждем примерно одну секунду

sleep(1);

// Переписываем имена серверов и другую информацию

GetServersName(); GetServersInfo(); } }

// ==================================================== // Деструктор класса SLIST // ====================================================

SLIST::~SLIST() {

// Отменяем ожидающие блоки ECB

for(int i=0;i<MAX_SERVERS;i++) { IPXCancelEvent(&Query[i].theECB); }

// Закрываем сокет

IPXCloseSocket(SrcSocket); }

// ==================================================== // Посылка SAP-запроса // ==================================================== void SLIST::SendSAPPacket(void) {



// Сбрасываем поле inUseFlag и ESRAddress, устанавливаем тип пакета 0

SendPacket.theECB.inUseFlag = 0; SendPacket.theECB.ESRAddress = 0; SendPacket.SAPq.packetType = 0;

// SAP-пакет состоит из одного фрагмента. Записываем в ECB // количество фрагментов, адрес и размер буфера

SendPacket.theECB.fragmentCount = 1; SendPacket.theECB.fragmentDescriptor[0].address = &SendPacket.SAPq; SendPacket.theECB.fragmentDescriptor[0].size = sizeof(SAPQueryPacket);

// Записываем в ECB номер своего сокета

SendPacket.theECB.socketNumber = SrcSocket;

// Устанавливаем адрес назначения - все станции в текущей сети, // сокет SAP_SOCKET. Устанавливаем поле непосредственного адреса

memset(SendPacket.SAPq.destination.network, '\x00', 4); memset(SendPacket.SAPq.destination.node, '\xFF', 6); SendPacket.SAPq.destination.socket = IntSwap(SAP_SOCKET); memset(SendPacket.theECB.immediateAddress, '\xFF', 6);

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

IPXGetInternetworkAddress(SendPacket.SAPq.source.network); SendPacket.SAPq.source.socket = IntSwap(SrcSocket);

// Заполняем передаваемый пакет. Устанавливаем тип запроса // и тип сервера

SendPacket.SAPq.queryType = IntSwap(QueryType); SendPacket.SAPq.serverType = IntSwap(0x0004);

// Посылаем SAP-пакет

IPXSendPacket(&SendPacket.theECB);

// Ожидаем завершения процесса передачи пакета

while (SendPacket.theECB.inUseFlag) IPXRelinquishControl();

// Сохраняем код возврата

errno = SendPacket.theECB.completionCode; }

// ==================================================== // Прием SAP-пакетов // ====================================================

void SLIST::ReceiveSAPPacket(RECEIVE_PACKET *Query) {

// Сбрасываем поле inUseFlag и ESRAddress

Query->theECB.inUseFlag = 0; Query->theECB.ESRAddress = 0; // Записываем в ECB количество фрагментов, адрес и размер буфера

Query->theECB.fragmentCount = 1; Query->theECB.fragmentDescriptor[0].address = &Query->SB; Query->theECB.fragmentDescriptor[0].size = sizeof(Query->SB);



// Устанавливаем в ECB свой номер сокета

Query->theECB.socketNumber = SrcSocket; }

// ==================================================== // Процедура переписывает имена серверов из тех // блоков ECB, для которых пришли пакеты // ====================================================

void SLIST::GetServersName(void) {

for(int i=0,j=0; i<MAX_SERVERS; i++) { if(!Query[i].theECB.inUseFlag) { strcpy(ServerName[j],Query[i].SB.ServerName); j++; } } }

// ==================================================== // Процедура получает информацию о серверах // ====================================================

void SLIST::GetServersInfo(void) {

// Получаем номера каналов первичного сервера // и сервера по умолчанию

PrimaryConnID = GetPrimaryConnectionID(); DefaultConnID = GetDefaultConnectionID();

// Цикл по всем обнаруженным в сети активным серверам

for(int i=0; i<MAX_SERVERS; i++) { if(ServerName[i][0]) {

// Получаем номер канала сервера

errno = GetConnectionID(ServerName[i], &ConnID[i]);

// Если канала нет, создаем его, подключаясь к серверу

if(errno) { AttachToFileServer(ServerName[i], &ConnID[i]); }

// Делаем текущий сервер предпочтительным, так как // именно к нему должны поступать запросы

errno = SetPreferredConnectionID(ConnID[i]);

// Получаем информацию о текущем сервере

if(!errno) errno = GetServerInformation(sizeof(ServerInfo[i]), &ServerInfo[i]);

// Получаем серийный номер и номер приложения

SerialNumber[i]=ApplicationNumber[i]=0L; errno = GetNetworkSerialNumber(&SerialNumber[i], &ApplicationNumber[i]); errno = 0; } } }

// ============================================================ // Процедура распечатывает имена и другую информацию о серверах // ============================================================

void SLIST::PrintServersName(void) {

// Цикл по всем обнаруженным в сети активным серверам

for(int i=0; i<MAX_SERVERS; i++) { if(ServerName[i][0]) {

// Выводим имя сервера

printf("%s",ServerInfo[i].serverName);



// Если номер канала текущего сервера совпадает с // номером канала первичного сервера, выводим строку "\t[Primary]"

if(ConnID[i] == PrimaryConnID) printf("\t[Primary]"); else printf("\t[ ]");

// Если номер канала текущего сервера совпадает с // номером канала сервера по умолчанию, выводим строку " [Default]"

if(ConnID[i] == DefaultConnID) printf(" [Default]"); else printf(" [ ]");

// Выводим версию сетевой операционной системы, // работающей на текущем сервере

printf(" v.%d.%d, ", ServerInfo[i].netwareVersion, ServerInfo[i].netwareSubVersion);

// Для подключенных серверов выводим серийный // номер и номер приложения

if(SerialNumber[i] != 0L) printf("s/n %08.8lX/%04.4X", SerialNumber[i], ApplicationNumber[i]); else printf("- Not Logged In -"); // Выводим номер канала, используемого для связи с текущим сервером

printf("\tConnID: %d,",ConnID[i]);

// Выводим максимальное число каналов, поддерживаемых // сервером, и количество используемых каналов

printf(" (%d-%d)\n", ServerInfo[i].maxConnectionsSupported, ServerInfo[i].connectionsInUse); } } }

Файл sap.hpp содержит все определения констант и описания структур, необходимые для программы SLIST. В частности, в этом файле описан класс SLIST.

// =================================================== // Листинг 5. Include-файл для программы SLIST.CPP // Файл slist!\sap.hpp // // (C) A. Frolov, 1993 // ===================================================

// Максимальное количество серверов, для которых выполняется опрос

#define MAX_SERVERS 8

// Типы сервиса SAP

#define GENERAL_SERVICE 1 #define NEAREST_SERVICE 3 #define NOT_SUPPORTED 1

// Короткоживущий сокет

#define SHORT_LIVED 0x00

// Сокет для SAP-протокола

#define SAP_SOCKET 0x452

// Тип пакета SAP

#define SAP_PACKET_TYPE 2

// Определения используемых типов данных

#define BYTE unsigned char #define WORD unsigned short

// Сетевой адрес

typedef struct IPXAddress { BYTE network[4]; BYTE node[6]; WORD socket; } IPXAddress;



// Заголовок IPX-пакета

typedef struct IPXHeader { WORD checkSum; WORD length; BYTE transportControl; BYTE packetType; IPXAddress destination; IPXAddress source; } IPXHeader;

// Заголовок SAP-пакета

typedef struct SAPHeader { WORD checksum; WORD length; BYTE transportControl; BYTE packetType; IPXAddress destination; IPXAddress source; WORD SAPPacketType; WORD serverType; BYTE serverName[48]; IPXAddress serverAddress; WORD interveningNetworks; } SAPHeader;

// Пакет для посылки SAP-запроса

typedef struct SAPQueryPacket { WORD checksum; WORD length; BYTE transportControl; BYTE packetType; IPXAddress destination; IPXAddress source; WORD queryType; WORD serverType; } SAPQueryPacket;

// Структуры для описания блока ECB

typedef struct ECBFragment { void far *address; WORD size; } ECBFragment;

typedef struct ECB { void far *linkAddress; void (far *ESRAddress)(); BYTE inUseFlag; BYTE completionCode; WORD socketNumber; BYTE IPXWorkspace[4]; BYTE driverWorkspace[12]; BYTE immediateAddress[6]; WORD fragmentCount; ECBFragment fragmentDescriptor[2]; } ECB;

// SAP-пакет

typedef struct { IPXHeader Header; WORD ResponseType; WORD ServerType; BYTE ServerName[48]; BYTE Network[4]; BYTE Node[6]; WORD Socket; WORD InterveningNetworks; } SAP;

// Структура для передачи SAP-пакета

typedef struct { ECB theECB; SAPQueryPacket SAPq; } SEND_PACKET;

// Структура для приема SAP-пакета

typedef struct { ECB theECB; SAP SB; } RECEIVE_PACKET;

// Информация о файл-сервере

typedef struct { char serverName[48]; BYTE netwareVersion; BYTE netwareSubVersion; WORD maxConnectionsSupported; WORD connectionsInUse; WORD maxVolumesSupported; BYTE revisionLevel; BYTE SFTLevel; BYTE TTSLevel; WORD peakConnectionsUsed; BYTE accountingVersion; BYTE VAPversion; BYTE queingVersion; BYTE printServerVersion; BYTE virtualConsoleVersion; BYTE securityRestrictionLevel; BYTE internetBridgeSupport; } FILE_SERV_INFO;

// Описания функций библиотеки NetWare C Interface

extern "C" int IPXInitialize(void); extern "C" int IPXOpenSocket(WORD *, BYTE); extern "C" int IPXListenForPacket(ECB *); extern "C" int IPXCancelEvent(ECB *); extern "C" int IPXCloseSocket(WORD); extern "C" WORD IntSwap(WORD); extern "C" void IPXGetInternetworkAddress(BYTE *); extern "C" void IPXSendPacket(ECB *); extern "C" void IPXRelinquishControl(void); extern "C" IPXGetLocalTarget(BYTE *, BYTE *, int*); extern "C" WORD IPXGetIntervalMarker(void); extern "C" long LongSwap(long); extern "C" int AttachToFileServer(char *, WORD *); extern "C" int SetPrimaryConnectionID(int); extern "C" int GetServerInformation(int, FILE_SERV_INFO *); extern "C" WORD GetPreferredConnectionID(void); extern "C" WORD GetPrimaryConnectionID(void); extern "C" WORD GetDefaultConnectionID(void); extern "C" int SetPreferredConnectionID(WORD); extern "C" int GetConnectionID(char *, WORD *); extern "C" void DetachFromFileServer(WORD); extern "C" int GetNetWareShellVersion(BYTE *,BYTE *, BYTE *); extern "C" int IsConnectionIDInUse(WORD); extern "C" int GetNetworkSerialNumber(long *, int*);



// Класс SLIST

class SLIST { private:

WORD QueryType; // тип запроса WORD SrcSocket; // сокет

// Массив для приема SAP-пакетов

RECEIVE_PACKET Query[MAX_SERVERS];

// Передаваемый SAP-пакет

SEND_PACKET SendPacket;

// Таблицы имен файл-серверов, серийных // номеров и номеров приложений

char ServerName[MAX_SERVERS][48]; long SerialNumber[MAX_SERVERS]; int ApplicationNumber[MAX_SERVERS];

// Таблица информации о файл-серверах

FILE_SERV_INFO ServerInfo[MAX_SERVERS];

// Таблица номеров каналов файл-серверов

WORD ConnID[MAX_SERVERS];

// Функции для приема и передачи SAP-пакетов

void ReceiveSAPPacket(RECEIVE_PACKET *Query); void SendSAPPacket(void); // Функции для получения имен файл-серверов и // другой информации о файл-серверах

void GetServersName(void); void GetServersInfo(void);

public:

int errno; // код ошибки WORD PreferredConnID; // предпочтительный сервер WORD PrimaryConnID; // первичный сервер WORD DefaultConnID; // сервер по умолчанию

BYTE MajorVersion; // верхний номер версии BYTE MinorVersion; // нижний номер версии BYTE Revision; // номер изменений

SLIST(int); // конструктор ~SLIST(); // деструктор

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

void PrintServersName(void);

// Проверка ошибок

int Error(void) { return errno; } };


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