Локальные сети персональных компьютеров. Использование протоколов 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]; }; Заголовок пакета подготавливается обычным
образом. В качестве номера сети можно указывать
либо действительный номер сети, либо нулевое
значение. В поле 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:
Расширенная структура сама по себе состоит из двух частей, имеющих соответственно, фиксированную и переменную структуру. Приведем формат фиксированной части: struct _EXTENDED_COMPONENT { unsigned char ComponentID; unsigned char NumberOfLocalNetworks; }; Поле 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 описывает тип сети:
Поле 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. |