Электронная библиотека книг Александра Фролова и Григория Фролова.
Shop2You.ru Создайте свой интернет-магазин
Библиотека
Братьев
Фроловых

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

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

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

2.7. Определение топологии сети

Если средствами IPX или SPX необходимо передавать данные между рабочими станциями, расположенными в разных сетях, соединенных мостами, вам не обойтись без определения топологии сети. Когда программа передает данные в пределах одной сети, она должна знать адрес станции, которой будет посылаться пакет.
В качестве номера сети можно указать нуль, при этом вам не надо будет даже знать номер сети, в которой расположена принимающая станция. Мы уже приводили примеры программ, передающих данные в пределах одной сети.

Другое дело, если в передачу данных вовлекается мост. Если пакет должен пройти мост, в поле ImmAddress блока ECB необходимо указать сетевой адрес моста, так как для того, чтобы попасть в другую сеть, пакет должен быть передан прежде всего в мост. В заголовке пакета при этом должен быть указан адрес принимающей станции - ее сетевой адрес, в том числе и номер сети, в которой расположена станция.

Вспомним, каким образом в приведенных ранее примерах программа-клиент и программа-сервер узнавали сетевой адрес друг друга.

Программа-клиент знала номер сокета, который используется программой-сервером. Клиент посылал пакет на этот сокет, при этом в качестве номера сети использовалось нулевое значение (пакет предназначен для передачи в пределах той сети, в которой находится передающая станция), а в качестве сетевого адреса станции - значение FFFFFFFFFFFFh (пакет предназначен для всех станций в сети). Сервер, расположенный в той же сети, что и клиент, принимал такой пакет. Анализируя поле "обратного адреса" пакета, сервер мог определить точный сетевой адрес клиента. Более того, в поле ImmAddress блока ECB, использовавшегося для приема пакета, стоял непосредственный адрес станции, от которой пришел пакет (пакет мог прийти и из другой сети через мост, в этом случае в поле ImmAddress стоял бы адрес моста).

Узнав сетевой адрес клиента, сервер отправлял ему пакет. Приняв пакет от сервера, клиент мог определить сетевой адрес сервера из заголовка пришедшего к нему пакета. Поле ImmAddress блока ECB, использовавшегося при приеме пакета от сервера, содержало непосредственный адрес станции, от которой пришел пакет.

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

Для того, чтобы пакет был принят всеми станциями сети, подключенной через мост, вам необходимо послать этот пакет в мост, указав в заголовке пакета номер сети, в которую передается пакет, а также адрес станции, равный FFFFFFFFFFFFh. Для того, чтобы послать пакет в мост, в поле ImmAddress соответствующего блока ECB надо указать адрес моста.

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

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

Сетевая оболочка, запущенная на рабочих станциях в сети Novell NetWare, может принимать пакеты на специальном диагностическом сокете с номером 0456h. В ответ на принятый пакет диагностический сервис возвращает станции, пославшей такой пакет, информацию о конфигурации сетевого программного и аппаратного обеспечения станции.

Основная идея определения конфигурации сети заключается в том, что программа-клиент посылает запрос о конфигурации одновременно всем станциям данной сети на сокете 0456h, указав в качестве номера сети нуль, а в качестве адреса станции значение FFFFFFFFFFFFh. Анализируя приходящую от станций диагностическую информацию, программа-клиент может обнаружить в сети мосты и определить как номера подключенных к мостам сетей, так и сетевые адреса самих мостов.

Зная сетевой адрес мостов и номера подключенных к ним сетей, программа-клиент сможет посылать запросы для поиска программы-сервера во все подключенные к мостам сети.

Очевидно, можно посылать диагностические запросы на сокете 0456h и в другие сети с целью поиска имеющихся там мостов. Таким образом можно выяснить конфигурацию всей сети и установить связь с программой-сервером, где бы она ни находилась.

Драйверы протоколов IPX и SPX обеспечивают два вида диагностического сервиса: IPX-диагностику и SPX-диагностику. Для определения конфигурации сети нужна только IPX-диагностика. SPX-диагностика предназначена в основном для измерений производительности сети и для получения уточненной информации о составе и конфигурации программного обеспечения рабочих станций. Подробное рассмотрение SPX-диагностики выходит за рамки нашей книги.

2.7.1. Диагностический сервис IPX

Программа может посылать диагностические запросы либо конкретной станции в сети, либо всем станциям, либо всем станциям, за исключением перечисленных в списке.

Для посылки диагностического запроса программа должна подготовить IPX-пакет, состоящий из обычного заголовка размером 30 байт и блока данных, имеющего следующую структуру:

struct _REQ {
        unsigned char Exclusions;
        unsigned char List[80][6];
};



Заголовок пакета подготавливается обычным образом. В качестве номера сети можно указывать либо действительный номер сети, либо нулевое значение.
В качестве сетевого адреса можно указывать либо адрес конкретной станции, либо адрес FFFFFFFFFFFFh. В поле сокета необходимо проставить значение 0456h.

В поле Exclusions блока данных необходимо проставить количество станций, от которых не требуется получать диагностику. Адреса таких станций должны быть перечислены в массиве List. Если вам надо получить диагностику от всех станций, укажите в поле Exclusions нулевое значение. В любом случае, если диагностика должна быть получена от нескольких станций, в качестве адреса в заголовке пакета необходимо указывать значение FFFFFFFFFFFFh.

Блок ECB для передачи диагностического запроса также подготавливается обычным образом. При первом диагностическом запросе в поле ImmAddress указывается значение FFFFFFFFFFFFh. В дальнейшем при определении конфигурации сети, подключенной через мост, в этом поле вы будете указывать сетевой адрес моста.

Важное замечание относительно сокета 0456h: вы не должны открывать или закрывать этот сокет. Диагностический сокет уже открыт, вы должны использовать его для формирования адреса при передаче диагностического запроса. Для приема ответных пакетов конфигурации (а также для передачи запроса) вам следует динамически получить от драйвера IPX другой сокет.

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

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

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

Приведем структуру первой части:

struct _RESPONSE {
        unsigned char MajorVersion;
        unsigned char MinorVersion;
        unsigned SPXDiagnosticSocket;
        unsigned char ComponentCount;
};



В полях MajorVersion и MinorVersion находится соответственно верхний и нижний номер версии диагностического сервиса.

Поле SPXDiagnosticSocket содержит номер сокета, который должен быть использован для SPX-диагностики.

Самое интересное поле - ComponentCount. В нем находится количество компонентов программного и аппаратного обеспечения, информация о которых имеется в принятом пакете конфигурации.

Далее в принятом пакете сразу за полем ComponentCount следуют структуры, описывающие отдельные компоненты. Они могут быть двух типов - простые и расширенные. Первое поле размером в один байт имеет одинаковое значение в обоих типах структур - это идентификатор компонента. По идентификатору компонента можно однозначно судить о том, какая используется структура - простая или расширенная.

Простая структура и в самом деле несложна. Она состоит всего из одного байта идентификатора компонента:

struct _SIMPLE_COMPONENT {
        unsigned char ComponentID;
};



Значениями поля ComponentID для простой структуры могут быть числа 0, 1, 2, 3 или 4:

Значение поля ComponentID Компонент
0 Драйвер IPX/SPX
1 Драйвер программного обеспечения моста
2 Драйвер сетевой оболочки рабочей станции
3 Сетевая оболочка
4 Сетевая оболочка в виде VAP-процесса

Расширенная структура сама по себе состоит из двух частей, имеющих соответственно, фиксированную и переменную структуру.

Приведем формат фиксированной части:

struct _EXTENDED_COMPONENT {
        unsigned char ComponentID;
        unsigned char NumberOfLocalNetworks;
};



Поле ComponentID может содержать значения 5, 6 или 7:

Значение поля ComponentID Компонент
5 Внешний мост
6 Файл-сервер с внутренним мостом
7 Невыделенный файл-сервер

Для определения конфигурации сети важно исследовать компоненты с типом 5, 6 и 7, так как именно они имеют отношение к соединениям сетей через мосты.

Переменная часть описывает сети, подключенные к компонентам с типом 5, 6 или 7. Количество таких сетей находится в поле NumberOfLocalNetworks фиксированной части.

Для описания сетей используется массив структур (размерностью NumberOfLocalNetworks):

struct _NETWORK_COMPONENT {
        unsigned char NetworkType;
        unsigned char NetworkAddress[4];
        unsigned char NodeAddress[6];
};



Поле NetworkType описывает тип сети:

Содержимое поля NetworkType Тип сети
0 Сеть, к которой подключен сетевой адаптер
1 Сеть с виртуальным сетевым адаптером (невыделенный файл-сервер)
2 Переназначенная удаленная линия (связь сетей через модемы)

Поле NetworkAddress содержит номер сети, к которой подключен соответствующий адаптер, а поле NodeAddress - сетевой адрес адаптера. Именно эти поля вам и нужны для определения номеров сетей, подключенных к мостам, и сетевых адресов самих мостов.

2.7.2. Пример программы

Приведем пример программы, которая демонстрирует способ определения конфигурации текущей сети, т. е. той сети, в которой работает данная программа. Программа создает 20 блоков ECB для приема пакетов конфигурации от рабочих станций, ставит их в очередь на прием пакетов и посылает диагностический пакет в текущую сеть по адресу FFFFFFFFFFFFh. Затем после небольшой задержки программа анализирует блоки ECB, поставленные в очередь на прием. Если был принят пакет конфигурации, он расшифровывается и в текстовом виде выводится в стандартный выходной поток.

Приводимая ниже программа - первая программа в серии "Библиотека системного программиста", составленная на языке С++. В ней мы использовали только некоторые возможности языка С++. Для более глубокого понимания объектно-ориентированного подхода в программировании вам необходимо ознакомиться с литературой, список которой приведен в конце книги.

В функции main() создается объект класса IPX_CLIENT, который по своим функциям является клиентом. В нашем случае задача клиента - послать диагностический запрос всем станциям сети и получить от них пакет конфигурации. Можно считать, что программа диагностики, работающая на каждой станции, является сервером. Принимая запросы от клиентов на диагностическом сокете, она посылает им в ответ пакеты конфигурации.

При создании объекта класса IPX_CLIENT вызывается конструктор, выполняющий все необходимые инициализирующие действия. Конструктор инициализирует драйвер IPX, получает его точку входа и открывает динамический сокет. Соответствующий деструктор автоматически закрывает полученный сокет при завершении работы программы.

Описание класса IPX_CLIENT находится в файле ipx.hpp (см. ниже).

После того как отработает конструктор объекта IPX_CLIENT, для созданного объекта вызывается функция go(), которая и выполняет все необходимые действия. В теле функции определен массив ECB *RxECB[20] из 20 указателей на объекты класса ECB. Эти объекты используются для приема пакетов конфигурации. Кроме того, определен один объект ECB TxECB для посылки пакета с диагностическим запросом.

Программа в цикле создает 20 объектов класса ECB и с помощью функции ListenForPacket() ставит их в очередь на прием пакетов. Затем программа посылает в сеть пакет с диагностическим запросом и ждет одну секунду. За это время станции сети присылают пакеты конфигурации.

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

Для упрощения программы мы ограничились диагностикой только одной сети. Кроме того, мы послали только один диагностический запрос, в котором не исключали ни одной станции. Если вам нужно определить конфигурацию всей сети, вам надо сделать следующее.

Во-первых, после приема пакетов конфигурации запишите адреса ответивших станций в список станций, исключаемых из диагностического запроса. Не забудьте проставить количество исключаемых станций. Затем выполните повторную передачу диагностического запроса. Выполняйте описанную процедуру до тех пор, пока в ответ на диагностический запрос не будет передан ни один пакет конфигурации. В этом случае адреса всех станций, имеющихся в текущей сети, будут записаны в список станций, исключаемых из диагностического запроса.

Во-вторых, выполните анализ пришедших пакетов конфигурации. Найдите пакеты, которые пришли от файл-серверов и мостов. Определите номера сетей, подключенных к ним. Затем выполните предыдущую процедуру многократной посылки диагностических пакетов в остальные сети. Для этого при передаче диагностического пакета в заголовке укажите номер сети, которую вы желаете проверить. В качестве сетевого адреса станции используйте значение FFFFFFFFFFFFh. В блоке ECB в поле непосредственного адреса укажите сетевой адрес моста, через который можно получить доступ в исследуемую сеть.

А теперь приведем текст основной программы (листинг 9):

// ===================================================
// Листинг 9. Вызов диагностики и определение
//           конфигурации текущей сети
//
// Файл ipxdiagn.cpp
//
// (C) A. Frolov, 1993
// ===================================================

#include <stdio.h>
#include <stdlib.h>
#include <conio.h>
#include <mem.h>
#include <string.h>
#include "ipx.hpp"

// Вход в программу.
// Создаем объект - программу-клиент. Затем запускаем ее.

void main(void) {
        IPX_CLIENT NetView;
        NetView.Go();
}

// Функция определяет и распечатывает конфигурацию текущей сети.

void IPX_CLIENT::Go(void) {

// Создаем 20 ECB для приема ответов от станций

        ECB *RxECB[20];

// Создаем ECB для передачи диагностического запроса.

        ECB TxECB(this->Socket, 0x456);

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

        for(int i=0; i<20; i++) {
                RxECB[i] = new ECB(this->Socket);
                RxECB[i]->ListenForPacket();
        }

// Посылаем диагностический пакет всем станциям текущей сети.

        TxECB.SendPacket();

        printf("*NetView* v1.0, (C) Фролов А.В., 1993\n"
                         "Подождите немного...\n\n");

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

        sleep(1);

// Распечатываем конфигурацию сети

        printf("Конфигурация сети:\n\n");
        printf("Версия\tСокет\tКомпоненты\tСеть\t\tУзел\n");
        printf("------\t-----\t----------\t----\t\t----\n");

        for(i=0; i<20; i++) {
                RxECB[i]->PrintDiagnostics();
        }
}



Файл ipx.hpp содержит определения классов для приведенной выше программы (листинг 10):

// ===================================================
// Листинг 10. Include-файл для работы с IPX
// Файл ipx.hpp
//
// (C) A. Frolov, 1993
// ===================================================

#include <mem.h>
#include <dos.h>

// -----------------------
// Команды интерфейса IPX
// -----------------------

#define IPX_CMD_OPEN_SOCKET                     0x00
#define IPX_CMD_CLOSE_SOCKET                    0x01
#define IPX_CMD_GET_LOCAL_TARGET                        0x02
#define IPX_CMD_SEND_PACKET                     0x03
#define IPX_CMD_LISTEN_FOR_PACKET               0x04
#define IPX_CMD_SCHEDULE_IPX_EVENT              0x05
#define IPX_CMD_CANCEL_EVENT                    0x06
#define IPX_CMD_GET_INTERVAL_MARKER             0x08
#define IPX_CMD_GET_INTERNETWORK_ADDRESS        0x09
#define IPX_CMD_RELINQUISH_CONTROL              0x0a
#define IPX_CMD_DISCONNECT_FROM_TARGET  0x0b

// -----------------------
// Коды ошибок
// -----------------------

#define NO_ERRORS               0
#define ERR_NO_IPX              1
#define ERR_NO_SPX              2
#define NO_LOGGED_ON            3
#define UNKNOWN_ERROR   0xff

// -----------------------
// Константы
// -----------------------

#define SHORT_LIVED     0
#define LONG_LIVED      0xff
#define IPX_DATA_PACKET_MAXSIZE 546

// Максимальный размер буфера данных

#define BUFFER_SIZE 512

// Внешние процедуры для инициализации и вызова драйвера IPX/SPX

extern "C" void far    ipxspx_entry(void far *ptr);
extern "C" int         ipx_init(void);
extern unsigned        IntSwap(unsigned i);

void IPXRelinquishControl(void);

// Структура для вызова драйвера IPX/SPX

struct IPXSPX_REGS {
                unsigned int    ax;
                unsigned int    bx;
                unsigned int    cx;
                unsigned int    dx;
                unsigned int    si;
                unsigned int    di;
                unsigned int    es;
};

// Класс динамических сокетов

class DYNAMIX_SOCKET {
public:
        unsigned errno;
        unsigned Socket;
        struct IPXSPX_REGS iregs;

// Конструктор динамического сокета.
// Открывает сокет и запоминает его номер.

        DYNAMIX_SOCKET() {
                iregs.bx = IPX_CMD_OPEN_SOCKET;
                iregs.dx = 0;
                iregs.ax = 0;
                ipxspx_entry( (void far *)&iregs );
                Socket = iregs.dx;
                errno = iregs.ax;
        };

// Деструктор. Закрывает ранее открытый сокет.

        ~DYNAMIX_SOCKET() {
                iregs.bx = IPX_CMD_CLOSE_SOCKET;
                iregs.dx = Socket;
                ipxspx_entry( (void far *)&iregs );
        };

};

// Класс программ-клиентов IPX

class IPX_CLIENT {
public:

        unsigned errno;

// Сокет, с которым работает программа-клиент

        DYNAMIX_SOCKET *Socket;

// Конструктор. Выполняет инициализацию клиента:
// инициализирует драйвер IPX и открывает динамический сокет.
        IPX_CLIENT() {
                if(ipx_init() != 0xff) {
                        errno = 0xff; return;
                }
                Socket = new DYNAMIX_SOCKET;
        }
// Деструктор. Автоматически закрывает
// сокет при завершении работы программы.

        ~IPX_CLIENT() {
                delete Socket;
        }

// Функция, определяющая конфигурацию сети

        void Go(void);
};

// Класс заголовков IPX-пакетов.

struct IPX_HEADER {

// Структура, описывающая заголовок

        struct _IPX_HEADER {
                unsigned int    Checksum;
                unsigned int    Length;
                unsigned char   TransportControl;
                unsigned char   PacketType;
                unsigned char   DestNetwork[4];
                unsigned char   DestNode[6];
                unsigned int    DestSocket;
                unsigned char   SourceNetwork[4];
                unsigned char   SourceNode[6];
                unsigned int    SourceSocket;
        } _ipx_header;

// Конструктор. Записывает в заголовок тип пакета,
// нулевой номер сети, в которую будет отправлен пакет,
// адрес 0xFFFFFFFFFFFF в качестве адреса назначения,
// номера сокетов адресата и отправителя пакета,

        IPX_HEADER(unsigned Socket, unsigned SrcSocket) {
                _ipx_header.PacketType = 4;
                memset(_ipx_header.DestNetwork, 0, 4);
                memset(_ipx_header.DestNode, 0xff, 6);
                _ipx_header.DestSocket = Socket;
                _ipx_header.SourceSocket = SrcSocket;
        }

// Конструктор. Записывает в заголовок тип пакета,
// нулевой номер сети, в которую будет отправлен пакет,
// адрес 0xFFFFFFFFFFFF в качестве адреса назначения.

        IPX_HEADER() {
                _ipx_header.PacketType = 4;
                memset(_ipx_header.DestNetwork, 0, 4);
                memset(_ipx_header.DestNode, 0xff, 6);
        }
};

// Класс блоков ECB.

struct ECB {

// Сам блок ECB в стандарте IPX/SPX.

                struct _ECB {
                        void far        *Link;
                        void far        (*ESRAddress)(void);
                        unsigned char   InUse;
                        unsigned char   CCode;
                        unsigned int    Socket;
                        unsigned int    ConnectionId;
                        unsigned int    RrestOfWorkspace;
                        unsigned char   DriverWorkspace[12];
                        unsigned char   ImmAddress[6];
                        unsigned int    FragmentCnt;
                        struct {
                                void far        *Address;
                                unsigned int Size;
                        } Packet[2];
                } _ecb;

// Указатель на заголовок пакета, связанного с данным ECB.

                struct IPX_HEADER *IPXHeader;

// Структура для приема ответа от станции
// после посылки диагностического пакета.

                struct Reply {
                        unsigned char MajVer;
                        unsigned char MinVer;
                        unsigned Socket;
                        unsigned char NumberOfComponents;
                        unsigned char Buffer[512];
                } Rep;

// Структура для хранения диагностического пакета.

                struct DiagnRequest {
                        unsigned char Exclusions;
                        unsigned char List[80][6];
                } DReq;

                struct IPXSPX_REGS iregs;

// Конструктор. Создается заголовок пакета,
// в блок ECB записывается номер сокета, используемого клиентом, 
// инициализируются счетчик фрагментов и дескрипторы фрагментов.
// В качестве непосредственного адреса указывается
// адрес 0xFFFFFFFFFFFF.

                ECB(DYNAMIX_SOCKET *Socket) {
                        IPXHeader = new IPX_HEADER;
                        memset(&_ecb, 0, sizeof(_ecb));
                        _ecb.Socket = Socket->Socket;
                        _ecb.FragmentCnt       = 2;
                        _ecb.Packet[0].Address = &(IPXHeader->_ipx_header);
                        _ecb.Packet[0].Size    = 30;
                        _ecb.Packet[1].Address = &Rep;
                        _ecb.Packet[1].Size    = sizeof(Rep);
                        memset(_ecb.ImmAddress, 0xff, 6);
                }

// Конструктор. Создается заголовок пакета, в блок ECB записывается 
// номер сокета, используемого клиентом, а также номер сокета 
// адресата, инициализируются счетчик фрагментов и дескрипторы 
// фрагментов. В качестве непосредственного адреса указывается
// адрес 0xFFFFFFFFFFFF.

                ECB(DYNAMIX_SOCKET *Socket, unsigned DstSocket) {

                        IPXHeader = new IPX_HEADER(IntSwap(DstSocket),
                                 Socket->Socket);

// Запрос адресуется всем станциям без исключения.

                        DReq.Exclusions = 0;

                        memset(&_ecb, 0, sizeof(_ecb));
                        _ecb.Socket = Socket->Socket;
                        _ecb.FragmentCnt       = 2;
                        _ecb.Packet[0].Address = &(IPXHeader->_ipx_header);
                        _ecb.Packet[0].Size    = 30;
                        _ecb.Packet[1].Address = &DReq;
                        _ecb.Packet[1].Size    = sizeof(DReq);
                        memset(_ecb.ImmAddress, 0xff, 6);
                }

// Прием IPX-пакета.

                void ListenForPacket(void) {
                        iregs.es = FP_SEG((void far*)&_ecb);
                        iregs.si = FP_OFF((void far*)&_ecb);
                        iregs.bx = IPX_CMD_LISTEN_FOR_PACKET;
                        ipxspx_entry( (void far *)&iregs );
                }

// Передача IPX-пакета.

                void SendPacket(void) {
                        iregs.es = FP_SEG((void far*)&_ecb);
                        iregs.si = FP_OFF((void far*)&_ecb);
                        iregs.bx = IPX_CMD_SEND_PACKET;
                        ipxspx_entry( (void far *)&iregs );
                }

// Распечатать принятый пакет конфигурации

                void PrintDiagnostics(void);
};



В файл ipx.cpp (листинг 11) мы вынесли остальные используемые программой функции, в частности функцию PrintDiagnostics(). Кроме того, программа вызывает функции, определенные в файле ipxdrv.asm, содержимое которого уже было приведено нами раньше.

// ===================================================
// Листинг 11. Функции IPX.
//
// Файл ipx.cpp
//
// (C) A. Frolov, 1993
// ===================================================

#include <stdio.h>
#include <stdlib.h>
#include <dos.h>
#include <conio.h>
#include "ipx.hpp"

/**
* .Name    IntSwap
*
* .Title   Обмен байтов в слове
*
* .Descr   Функция меняет местами байты в слове,
*          которое передается ей в качестве параметра
*
* .Params  unsigned i - преобразуемое слово
*
* .Return  Преобразованное слово
**/

unsigned IntSwap(unsigned i) {
        return((i>>8) | (i & 0xff)<<8);
}

/**
* .Name    IPXRelinquishControl
*
* .Title   Передать управление IPX при ожидании
*
* .Descr   Функция используется при ожидании
*          завершения приема через опрос поля InUse
*          блока ECB.
*
* .Params  Не используются
*
* .Return  Ничего
**/

void IPXRelinquishControl(void) {

        struct IPXSPX_REGS iregs;

        iregs.bx = IPX_CMD_RELINQUISH_CONTROL;
        ipxspx_entry( (void far *)&iregs );
}

// Функция для печати содержимого принятого пакета конфигурации.

void ECB::PrintDiagnostics(void) {
        int i, j, k, networks, component;

// Печатаем конфигурацию только для тех ECB,в поле InUse которых 
// стоит нулевое значение, т.е. если был принят пакет.

                if(!_ecb.InUse) {
// Распечатываем версию диагностической поддержки, номер сокета для 
// SPX-диагностики и количество компонентов программного 
//обеспечения, работающего на станции.

                        printf("\n%d.%d\t%d\t%d\t\t",
                                Rep.MajVer, Rep.MinVer,
                                Rep.Socket, Rep.NumberOfComponents);

// Распечатываем номер сети, из которой пришел пакет конфигурации.

                        for(i=0;i<4;i++) {
                          printf("%02.2X",(unsigned char)
                                IPXHeader->_ipx_header.SourceNetwork[i]);
                        }
                        printf("\t");
// Распечатываем сетевой адрес станции, из
// которой пришел пакет конфигурации.

                        for(i=0;i<6;i++) {
                          printf("%02.2X",(unsigned char)
                                IPXHeader->_ipx_header.SourceNode[i]);
                        }
                        printf("\n\n");

// Для каждого программного компонента распечатываем его название.

                        for(i=0;i<Rep.NumberOfComponents;) {
                                switch(component=Rep.Buffer[i]) {
                                case 0:
                                        printf("\tДрайвер IPX/SPX\n");
                                        i++;
                                        break;
                                case 1:
                                        printf("\tДрайвер моста\n");
                                        i++;
                                        break;
                                case 2:
                                        printf("\tДрайвер сетевой оболочки\n");
                                        i++;
                                        break;
                                case 3:
                                        printf("\tСетевая оболочка\n");
                                        i++;
                                        break;
                                case 4:
                                        printf("\tОболочка VAP\n");
                                        i++;
                                        break;

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

                          case 5: case 6: case 7:
                                  switch(component) {
                                  case 5:
                                          printf("\tВыделенный мост\n");
                                          break;
                                  case 6:
                                          printf("\tФайл-сервер/внутренний  мост\n");
                                          break;
                                  case 7:
                                          printf("\tНевыделенный сервер\n");
                                          break;
                                        }
                                        i++;
// Количество подключенных сетей

                                        printf("\t\tПодключено сетей: %d",
                                                (unsigned char)Rep.Buffer[i]);
                                        networks = Rep.Buffer[i];
                                        i++;

// Для каждой сети печатаем ее тип,
// номер сети и сетевой адрес адаптера.

                                        for(j=0;j<networks;j++) {

// Тип сети

                                                printf("\n\t\t\tТип сети: %d\t",
                                                        (unsigned char)Rep.Buffer[i++]);

// Номер сети

                             for(k=0;k<4;k++,i++) {
                               printf("%02.2X",(unsigned char) Rep.Buffer[i]);
                             }
                             printf("\t");
// Сетевой адрес адаптера

                             for(k=0;k<6;k++,i++) {
                               printf("%02.2X",(unsigned char) Rep.Buffer[i]);
                             }
                                      }
                                        printf("\n");
                                        break;
                                }
                        }

                }
}



Приведем образец листинга, выдаваемого программой в стандартный поток вывода:

*NetView* v1.0, (C) Фролов А.В., 1993
Подождите немного...

Конфигурация сети:

Версия  Сокет   Компоненты      Сеть    Узел
------  -----   ----------      ----    ----

1.0     576     3       00000010        000000000001

                Драйвер IPX/SPX
                Драйвер моста
                Файл-сервер/внутренний мост
                Подключено сетей: 3
                Тип сети: 1     00000010        000000000001
                Тип сети: 0     00000013        48450000456C
                Тип сети: 0     00000012        4845000047C7

1.0     576     3       0000000E        000000000001

                Драйвер IPX/SPX
                Драйвер моста
                Файл-сервер/внутренний мост
                Подключено сетей: 2
                Тип сети: 1     0000000E        000000000001
                Тип сети: 0     00000012        008428801E9D

1.1     320     3       00000012        008058801C82

                Драйвер IPX/SPX
                Драйвер сетевой оболочки
                Сетевая оболочка

1.1     320     3       00000012        484500004666

                Драйвер IPX/SPX
                Драйвер сетевой оболочки
                Сетевая оболочка

1.0     320     3       00000012        484500004889

                Драйвер IPX/SPX
                Драйвер сетевой оболочки
                Сетевая оболочка

1.1     320     3       00000012        008058801F0F

                Драйвер IPX/SPX
                Драйвер сетевой оболочки
                Сетевая оболочка

1.1     320     3       00000012        000561E5D284

                Драйвер IPX/SPX
                Драйвер сетевой оболочки
                Сетевая оболочка

1.1     320     3       00000012        008058801E1D

                Драйвер IPX/SPX
                Драйвер сетевой оболочки
                Сетевая оболочка

1.1     320     3       00000012        484506004726

                Драйвер IPX/SPX
                Драйвер сетевой оболочки
                Сетевая оболочка

1.1     320     3       00000012        008058801EB5

                Драйвер IPX/SPX
                Драйвер сетевой оболочки
                Сетевая оболочка

1.0     320     3       00000012        484556004705

                Драйвер IPX/SPX
                Драйвер сетевой оболочки
                Сетевая оболочка



Из приведенного листинга видно, что в сети имеются два файл-сервера и 9 рабочих станций. Все рабочие станции находятся в сети 00000012. К этой же сети подключены оба файл-сервера. Первый в списке файл-сервер подключен к сетям 00000012 и 00000013, следовательно, этот файл-сервер является внутренним мостом между сетью 00000012 и 00000013.

Обратите внимание, что для двух файл-серверов, имеющихся в сети, указан тип сети 1 и номера сетей 10h и 0Eh. Это так называемые внутренние номера сетей, которые задавались при генерации Novell NetWare версии 3.11. Физические сети имеют в нашем случае номера 12 и 13.

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