15 мая 2023 года "Исходники.РУ" отмечают своё 23-летие!
Поздравляем всех причастных и неравнодушных с этим событием!
И огромное спасибо всем, кто был и остаётся с нами все эти годы!

Главная Форум Журнал Wiki DRKB Discuz!ML Помощь проекту

Библиотека Системного Программиста. Том 8
Локальные сети персональных компьютеров. Использование протоколов IPX, SPX, NETBIOS

4. ПРОТОКОЛ NETBIOS

4.1. Адресация станций и программ

4.2. Работа с протоколом NETBIOS

4.3. Команды NETBIOS

4.4. Коды ошибок

4.5. Система "клиент-сервер" на базе датаграмм

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

Последний протокол, который мы рассмотрим в нашей книге, - протокол NETBIOS (Network Basic Input/Output System - базовая сетевая система ввода/вывода), разработанный IBM. Этот протокол работает на трех уровнях семиуровневой модели OSI: сетевом уровне, транспортном уровне и на уровне каналов связи. Уровень каналов связи обеспечивает механизм обмена сообщениями между программами, работающими на станциях в рамках канала связи или сессии. NETBIOS может обеспечить интерфейс более высокого уровня, чем протоколы IPX и SPX.

Протокол NETBIOS поддерживается в сетях IBM (IBM PC LAN), Novell NetWare, Microsoft Windows for Workgroups и в других сетях. К сожалению, нет единого стандарта на протокол NETBIOS, поэтому в сетевом программном обеспечении разных фирм используются разные интерфейсы для вызова команд NETBIOS.

С нашей точки зрения, наибольший интерес представляет применение NETBIOS в сетях Novell NetWare и Microsoft Windows for Workgroups. Мы рассмотрим основные возможности NETBIOS, связанные с передачей данных между рабочими станциями в пределах одного логического сегмента сети.

Для работы с протоколом NETBIOS в сетях Novell NetWare необходимо запустить специальный эмулятор NETBIOS - программу netbios.exe, входящую в комплект поставки Novell NetWare. Эта программа эмулирует протокол NETBIOS с использованием уже знакомых нам протоколов IPX/SPX.

Использовать NETBIOS проще, чем IPX или SPX. Однако, так как в среде Novell NetWare нужен специальный эмулятор NETBIOS, эффективность работы программы может снизиться. Кроме того, для эмулятора нужна дополнительная память, так как он реализован в виде резидентной программы.

4.1. Адресация станций и программ

Как вы помните, для идентификации рабочей станции протоколы IPX и SPX используют номер сети, адрес станции в сети и сокет. Адрес станции определяется на аппаратном уровне и представляет собой число длиной 6 байт. Номер сети занимает 4 байта. Сокеты выделяются динамически драйвером протокола IPX или могут быть получены в Novell.

Протокол NETBIOS использует другой механизм адресации станций и программ. Для адресации станции используются имена размером 16 байт. Каждая станция имеет одно постоянное имя (permanent name), которое образуется из аппаратного адреса добавлением к нему слева десяти нулевых байт. Кроме постоянного имени протокол NETBIOS позволяет добавлять (и удалять) обычные имена и групповые имена. Обычные имена служат для идентификации рабочей станции, групповые могут служить для посылки пакетов одновременно нескольким станциям в сети. Постоянное имя удалить нельзя, так как оно полностью определяется аппаратным обеспечением станции.

При добавлении обычного имени протокол NETBIOS опрашивает всю сеть для проверки уникальности имени. Групповое имя может быть одинаковое на нескольких станциях, поэтому при добавлении группового имени опрос сети
не выполняется.

После добавления нового имени этому имени присваивается так называемый номер имени (name number), который используется для передачи данных по сети.

Сравнивая методы адресации, используемые протоколами IPX/SPX и NETBIOS, нетрудно заметить, что метод адресации протокола NETBIOS более удобен. Вы можете адресовать данные не только одной станции (как в IPX и SPX) или всем станциям сразу (как в IPX), но и группам станций, имеющим одинаковое групповое имя. Это может быть удобно, если в сети работают несколько групп пользователей, которые интенсивно обмениваются данными между собой.

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

4.2. Работа с протоколом NETBIOS

Протокол NETBIOS предоставляет программам интерфейс для передачи данных на уровне датаграмм и на уровне каналов связи. Для вызова NETBIOS программа должна создать в памяти управляющий блок, который называется NCB (Network Control Block - сетевой управляющий блок). Адрес заполненного блока NCB передается прерыванию INT 5Ch, в рамках которого и реализован интерфейс протокола NETBIOS. Есть также альтернативный интерфейс, реализованный в рамках прерывания INT 2Ah, который поддерживается эмулятором NETBIOS, разработанным фирмой Novell, а также операционной системой Windows for Workgroups версии 3.1.

4.2.1. Проверка присутствия NETBIOS

Первое, что должна сделать программа, желающая воспользоваться протоколом NETBIOS, - проверить наличие в системе интерфейса NETBIOS.

Ниже приведена программа, которая определяет, установлен ли драйвер NETBIOS.

С помощью функции getvect() программа получает указатель на обработчик прерывания INT 5Ch. Это прерывание используется для вызова NETBIOS. Если сегментная компонента адреса равна нулю или F000h, обработчик прерывания не установлен или установлена заглушка, расположенная в BIOS. В этом случае программа считает, что NETBIOS отсутствует.

// ===================================================
// Листинг 16. Проверка присутствия NETBIOS
//
// Файл nbver.cpp
//
// (C) A. Frolov, 1993
// ===================================================

#include <stdio.h>
#include <stdlib.h>
#include <dos.h>
#include <conio.h>
#include <mem.h>
#include <string.h>

void main(void) {
        void interrupt ( *int5C)(...);

        printf("Check if NETBIOS is installed\n");
        int5C = getvect(0x5c);
        if(FP_SEG(int5C) == 0x0000 || FP_SEG(int5C) == 0xF000) {
                printf("NETBIOS NOT installed.\n");
        }
        else printf("NETBIOS is installed!\n");

}

Другой способ проверки наличия интерфейса NETBIOS заключается в вызове прерывания INT 2Ah. Загрузите в регистр AH нулевое значение и вызовите прерывание INT 2Ah. Если после возврата из прерывания в регистре AH
попрежнему находится нуль, драйвер NETBIOS не установлен.

Данный способ проверки будет работать на виртуальной машине DOS, запущенной в среде Windows for Workgroups версии 3.1 (если Windows работает в расширенном режиме).

Приведенная ниже программа определяет присутствие NETBIOS с помощью вызова прерывания INT 2Ah (листинг 17):

// ===================================================
// Листинг 17. Проверка присутствия NETBIOS
//             с использованием интерфейса INT 2Ah
//
// Файл 2atest.cpp
//
// (C) A. Frolov, 1993
// ===================================================

#include <stdio.h>
#include <stdlib.h>
#include <dos.h>
#include <conio.h>
#include <mem.h>
#include <string.h>

void main(void) {

        union REGS regs;

        printf("Check if NETBIOS is installed\n");

        regs.h.ah = 0;
        int86(0x2a, &regs, &regs);

        if(regs.h.ah  == 0) {
                printf("NETBIOS NOT installed.\n");
        }
        else printf("NETBIOS is installed!\n");

}

Прерывание INT 2Ah используется в сетях фирм Microsoft и Lantastic. Эмулятор протокола NETBIOS, поставляющийся фирмой Novell вместе с операционной системой Novell NetWare, поддерживает как интерфейс прерывания INT 5Ch, так и интерфейс INT 2Ah.

4.2.2. Вызов команд протокола NETBIOS

Интерфейс протокола NETBIOS реализован в рамках прерывания INT 5Ch или INT 2Ah и очень прост.

Для вызова команд протокола NETBIOS вам достаточно подготовить блок NCB, загрузить его дальний адрес в регистры ES:BX и вызвать прерывание INT 5Ch.

Приведем формат вызова функции прерывания INT 2Ah, предназначенной для выполнения команд NETBIOS:

На входе:AH =04h.
AL= Признак автоматического повтора команды при получении кодов ошибки 09h (недостаточно ресурсов), 12h (создание канала отвергнуто, так как на другом конце
не выдана команда NB_Listen), 21h (занят интерфейс):
00h - повторять команду;
FFh - не повторять команду.
ES:BX= Адрес заполненного блока NCB.
На выходе:AH= Код завершения:
00h - команда выполнилась без ошибок;
01h - при выполнении команды были ошибки.
AL= Если содержимое регистра AH после возврата из прерывания не равно нулю, регистр AL содержит код ошибки.

Вы можете использовать описанный выше вызов прерывания INT 2A для вызова NETBIOS из программы, работающей на виртуальной машине Windows for Workgroups версии 3.1.

Для вызова команд протокола NETBIOS из пограммы, составленной на языке Си, вы можете воспользоваться обычными средствами вызова программных прерываний, такими, как функция int86x().

4.2.3. Формат блока NCB

Приведем формат блока NCB:

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];
};

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

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

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

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

Поле Buffer представляет собой дальний указатель в формате [сегмент:смещение] на буфер, который должен содержать данные перед выполнением передачи или на буфер, который будет использован для приема данных.

Размер буфера, используемого для приема или передачи данных, определяется содержимым поля Size.

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

Поле OurName должно содержать имя вашей программы, под которым она будет принимать данные. В качестве этого имени может выступать обычное, групповое или постоянное имя.

Поля ReceiveTimeout и SendTimeout содержат интервал времени (измеряемый в 1/2 с), в течение которого ожидается завершение соответственно команд приема и передачи.

Поле PostRoutine - указатель на программу, которая получает управление после завершения команды. Эта программа (POST-программа) аналогична программе ESR протоколов IPX/SPX и вызывается только в том случае, если в поле PostRoutine был указан адрес программы. Если же это поле заполнить нулями, никакая программа вызываться не будет.

Поле AdapterNumber используется, если в станции установлено несколько сетевых адаптеров (в сетях Ethernet этого обычно не бывает). В этом поле указывается номер адаптера, для которого предназначена команда. Первый адаптер имеет номер 0, второй - 1.

Поле FinalCCode содержит во время выполнения команды значение 0xFF. После завершения выполнения команды в это поле записывается код ошибки, который относится к выполнению команды в целом (в отличие от кода в поле CCode). Если ваша программа не задала адрес для программы в поле PostRoutine, она должна опрашивать в цикле содержимое этого поля, ожидая, пока в нем не появится значение, отличное от 0xFF.

Поле Reserved зарезервировано для использования протоколом NETBIOS, ваша программа не должна изменять его содержимое.

4.2.4. POST-программа

POST-программа является программой обработки прерывания. Она получает управление в состоянии с запрещенными прерываниями. Регистры ES:BX содержат адрес блока NCB, который использовался при выполнении команды.
В регистр AL записано значение из поля FinalCCode блока NCB.

Учтите, что, как и из любой другой программы обработки прерывания, из POST-программы не следует вызывать функции MS-DOS.

Требования к POST-программе во многом такие же, как и к ESR-программе, используемой протоколами IPX и SPX. Она должна позаботиться о сохранении изменяемых регистров, установить регистр DS на сегмент данных программы (для обеспечения доступа к переменным). POST-программа должна работать как можно быстрее. Лучше всего если она будет использоваться только для установки флага, сигнализирующего основной программе о завершении выполнения команды.

Перед завершением своей работы POST-программа должна восстановить содержимое всех регистров и выполнить команду возврата из прерывания IRET.

Если вы составляете POST-программу на языке программирования Си, вы можете воспользоваться ключевым словом interrupt:

void interrupt NETBIOS_Post_Routine(void);

Лучше всего составить POST-программу на языке ассемблера, например, так:

.286
.MODEL SMALL

.DATA

        _completed_ncb_ptr dd 0

.CODE

        PUBLIC   _netbios_post
        PUBLIC  _completed_ncb_ptr

_netbios_post PROC FAR

                push ax
                push    ds
                push es
                push si

                mov     ax, DGROUP
                mov     ds, ax

                mov     word ptr _completed_ncb_ptr+2, es
                mov     word ptr _completed_ncb_ptr, si

                pop     si
                pop     es
                pop     ds
                pop     ax

                iret
_netbios_post ENDP

end

4.3. Команды NETBIOS

Перед выполнением команды ее код должен быть записан в поле Cmd блока NCB. Каждая команда NETBIOS реализована в двух вариантах - с ожиданием и без ожидания.

Если вашей программе нечего делать до тех пор, пока выполнение команды NETBIOS не будет полностью завершено, вы можете выбрать вариант с ожиданием. В этом случае после вызова NETBIOS программа вновь получит управление только после завершения выполнения команды. При использовании протоколов IPX/SPX ваша программа должна была сама дожидаться выполнения
вызванной функции, выполняя в цикле опрос поля InUse блока ECB. Однако учтите, что, если по каким-либо причинам выполнение команды не может быть завершено, ваша программа "зависнет".

Вариант без ожидания похож на вариант использования функций IPX/SPX с программой ESR, вызываемой после завершения операции. Программа, вызвавшая команду NETBIOS без ожидания, получает управление немедленно. Команда будет выполняться в фоновом режиме параллельно с работой вызвавшей ее программы. После того как выполнение команды будет завершено, управление будет передано функции, адрес которой необходимо указать в поле PostRoutine блока NCB. Можно также дожидаться окончания выполнения команды, опрашивая в цикле поле FinalCCode, которое будет содержать значение 0xFF до тех пор, пока команда не будет выполнена.

Все команды NETBIOS можно разделить на несколько групп:

для работы с именами;

  • для приема и передачи датаграмм;
  • для работы с каналами;
  • для приема и передачи данных через каналы;
  • другие команды.

Так как большинство команд NETBIOS реализованы в двух вариантах
(с ожиданием и без ожидания), для обозначения варианта с ожиданием мы будем в названии команды после префикса NB_ (от слова NETBIOS) добавлять букву W (от слова wait - ожидание). Например, команда NB_AddName выполняется без ожидания, а команда NB_WAddName - с ожиданием.

У команд без ожидания старший бит кода команды установлен в единицу.

4.3.1. Работа с именами

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

NB_WAddName (0x30)

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

Если длина имени меньше 16 байт, оно должно быть дополнено справа символами пробела. Можно закрыть имя двоичным нулем для совместимости со строками языка Си. Нуль должен находиться в последней позиции имени.

После успешного выполнения команды NETBIOS присваивает имени номер и возвращает его в поле LocalSessionNumber блока NCB. Номер имени может потребоваться вам для работы с датаграммами.

В процессе добавления имени NETBIOS посылает по сети запрос. Если такое имя уже используется на какой-либо станции, эта станция пришлет ответ.
В этом случае команда завершиться с ошибкой и имя добавлено не будет.

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

Поля NCB на входе Содержимое
Cmd0x30
OurNameДобавляемое имя
PostRoutineДальний указатель на POST-программу или нулевое значение, если POST-программа не используется
AdapterNumberНомер адаптера; 0 - первый адаптер, 1 - второй

Поля NCB на выходе Содержимое
NetworkNameNumberПрисвоенный номер имени
CCodeПромежуточный код завершения команды
FinalCCodeОкончательный код завершения команды

Содержимое поля CCode на выходе Значение
0x00Нет ошибок
0x03Неправильный код команды
0x21Интерфейс занят
0x22Выдано слишком много команд
0x23Неправильное значение в поле номера адаптера AdapterNumber
0x40 - 0x4FНеобычное состояние сети (Unusual network condition)
0x50 - 0xFEСбой сетевого адаптера или другого сетевого аппаратного обеспечения

Содержимое поля FinalCCode на выходе Значение
0x00Нет ошибок
0x03Неправильный код команды
0x09Нет доступных ресурсов
0x0DУказанное команде имя уже используется на этой станции
0x0EПереполнение таблицы имен
0x15Неправильное имя
0x16Имя уже используется на одной из рабочих станций в сети
0x19Конфликт имен (внутренняя ошибка NETBIOS)
0x21Интерфейс занят
0x22Выдано слишком много команд
0x23Неправильное значение в поле номера адаптера AdapterNumber
0x40 - 0x4FНеобычное состояние сети (Unusual network condition)
0x50 - 0xFEСбой сетевого адаптера или другого сетевого аппаратного обеспечения

NB_AddName (0xB0)

Команда аналогична предыдущей, за исключением того, что она выполняется без ожидания и в поле Cmd необходимо записать значение 0xB0.

NB_WAddGroupName (0x36)

Команда добавляет указанное в поле OurName групповое имя в таблицу имен, расположенную на рабочей станции.

Имя не должно использоваться другими станциями в сети как обычное. Однако несколько станций могут использовать одно и тоже имя как групповое.

Если длина имени меньше 16 байт, оно должно быть дополнено справа символами пробела. Можно закрыть имя двоичным нулем для совместимости со строками языка Си. Нуль должен находиться в последней позиции имени.

После успешного выполнения команды NETBIOS присваивает имени номер и возвращает его в поле LocalSessionNumber блока NCB. Номер имени нужен для работы с датаграммами.

Поля NCB на входе Содержимое
Cmd0x36
OurNameДобавляемое групповое имя
PostRoutineДальний указатель на POST-программу или нулевое значение, если POST-программа не используется
AdapterNumberНомер адаптера; 0 - первый адаптер, 1 - второй

Поля NCB на выходе >Содержимое
NetworkNameNumberПрисвоенный номер имени
CCodeПромежуточный код завершения команды
FinalCCodeОкончательный код завершения команды

Содержимое поля CCode на выходе Значение
0x00Нет ошибок
0x03Неправильный код команды
0x21Интерфейс занят
0x22Выдано слишком много команд
0x23Неправильное значение в поле номера адаптера AdapterNumber
0x40 - 0x4FНеобычное состояние сети (Unusual network condition)
0x50 - 0xFEСбой сетевого адаптера или другого сетевого аппаратного обеспечения

Содержимое поля FinalCCode на выходе Значение
0x00Нет ошибок
0x03Неправильный код команды
0x09Нет доступных ресурсов
0x0DУказанное команде имя уже используется на этой станции
0x0EПереполнение таблицы имен
0x15Неправильное имя
0x16Имя уже используется на одной из рабочих станций
в сети
0x19Конфликт имен (внутренняя ошибка NETBIOS)
0x21Интерфейс занят
0x22Выдано слишком много команд
0x23Неправильное значение в поле номера адаптера AdapterNumber
0x40 - 0x4FНеобычное состояние сети (Unusual network condition)
0x50 - 0xFEСбой сетевого адаптера или другого сетевого аппаратного обеспечения

NB_AddGroupName (0xB6)

Команда аналогична предыдущей, за исключением того, что она выполняется без ожидания и в поле Cmd необходимо записать значение 0xB6.

NB_WDeleteName (0x31)

Команда удаляет имя из таблицы имен рабочей станции, если оно не используется каким-либо каналом. Если же имя используется каналом, то оно помечается как назначенное для удаления и удаляется после закрытия канала.

Если вы попытаетесь удалить имя, которое используется каналом, команда завершится с кодом ошибки 0xF. В этом случае перед удалением имени необходимо закрыть канал (см. дальше описание команд для работы с каналами). Как только канал будет закрыт, связанное с ним имя будет автоматически удалено, если перед закрытием канала выполнялась попытка удалить имя.

Если имя используется несколькими каналами, его можно удалить только после закрытия всех связанных с ним каналов.

Поля NCB на входе Содержимое
Cmd0x31
OurNameУдаляемое имя
PostRoutineДальний указатель на POST-программу или нулевое значение, если POST-программа не используется
AdapterNumberНомер адаптера; 0 - первый адаптер, 1 - второй
Поля NCB на выходе Содержимое
CCodeПромежуточный код завершения команды
FinalCCodeОкончательный код завершения команды

Содержимое поля CCode на выходе Значение
0x00Нет ошибок
0x03Неправильный код команды
0x21Интерфейс занят
0x22Выдано слишком много команд
0x23Неправильное значение в поле номера адаптера AdapterNumber
0x40 - 0x4FНеобычное состояние сети (Unusual network condition)
0x50 - 0xFEСбой сетевого адаптера или другого сетевого аппаратного обеспечения

Содержимое поля FinalCCode на выходе Значение
0x00Нет ошибок
0x03Неправильный код команды
0x0FИмя используется каналом. Немедленное удаление имени невозможно, однако оно отмечено как назначенное для удаления и будет удалено после закрытия канала
0x15Неправильное имя
0x21Интерфейс занят
0x22Выдано слишком много команд
0x23Неправильное значение в поле номера адаптера AdapterNumber
0x40 - 0x4FНеобычное состояние сети (Unusual network condition)
0x50 - 0xFEСбой сетевого адаптера или другого сетевого аппаратного обеспечения

NB_DeleteName (0xB1)

Команда аналогична предыдущей, за исключением того, что она выполняется без ожидания и в поле Cmd необходимо записать значение 0xB1.

4.3.2. Прием и передача датаграмм

С помощью команд приема и передачи датаграмм вы можете передавать и принимать пакеты без подтверждения, аналогично тому, как это выполняет протокол IPX.

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

В отличие от протокола IPX протокол NETBIOS использует разные команды для приема обычных датаграмм и датаграмм, адресованных всем станциям в сети.

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

Длина сообщений, передаваемых при помощи команд данной группы, ограничена 512 байтами. Через каналы вы можете передавать блоки данных существенно большего размера.

NB_WSendDatagram (0x20)

Команда предназначена для передачи блока данных размером от 1 до 512 байт в виде датаграммы (без подтверждения приема). Датаграмма может быть послана на обычное или групповое имя.

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

Поля NCB на входе Содержимое
Cmd0x20
NetworkNameNumberНомер, присвоенный при добавлении имени
CallNameИмя станции, которой передаются данные
BufferАдрес буфера, содержащего передаваемые данные
SizeРазмер буфера
PostRoutineДальний указатель на POST-программу или нулевое значение, если POST-программа не используется
AdapterNumberНомер адаптера; 0 - первый адаптер, 1 - второй

Поля NCB на выходе Содержимое
CCodeПромежуточный код завершения команды
FinalCCodeОкончательный код завершения команды

Содержимое поля CCode на выходе Значение
0x00Нет ошибок
0x03Неправильный код команды
0x21Интерфейс занят
0x22Выдано слишком много команд
0x23Неправильное значение в поле номера адаптера AdapterNumber
0x40 - 0x4FНеобычное состояние сети (Unusual network condition)
0x50 - 0xFEСбой сетевого адаптера или другого сетевого аппаратного обеспечения

Содержимое поля FinalCCode на выходе Значение
0x00Нет ошибок
0x01Неправильная длина буфера
0x03Неправильный код команды
0x13Неправильный номер имени
0x19Конфликт имен (внутренняя ошибка NETBIOS)
0x21Интерфейс занят
0x22Выдано слишком много команд
0x23Неправильное значение в поле номера адаптера AdapterNumber
0x40 - 0x4FНеобычное состояние сети (Unusual network condition)
0x50 - 0xFEСбой сетевого адаптера или другого сетевого аппаратного обеспечения

NB_SendDatagram (0xA0)

Команда аналогична предыдущей, за исключением того, что она выполняется без ожидания и в поле Cmd необходимо записать значение 0xA0.

NB_WSendBroadcastDatagram (0x22)

Команда посылает датаграмму, которую примут все станции, выдавшие команду NB_ReceiveBroadcastDatagram (в том числе и передающая станция, если она тоже выдала команду NB_ReceiveBroadcastDatagram).

Если на одной станции команда NB_ReceiveBroadcastDatagram выдана несколько раз, все буферы после приема данных будут содержать одну и ту же информацию.

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

Поля NCB на входе Содержимое
Cmd0x22
NetworkNameNumberНомер, присвоенный при добавлении имени
BufferАдрес буфера, содержащего передаваемые данные
SizeРазмер буфера
PostRoutineДальний указатель на POST-программу или нулевое значение, если POST-программа не используется
AdapterNumberНомер адаптера; 0 - первый адаптер, 1 - второй

Поля NCB на выходе Содержимое
CCodeПромежуточный код завершения команды
FinalCCodeОкончательный код завершения команды

Содержимое поля CCode на выходе Значение
0x00Нет ошибок
0x03Неправильный код команды
0x21Интерфейс занят
0x22Выдано слишком много команд
0x23Неправильное значение в поле номера адаптера AdapterNumber
0x40 - 0x4FНеобычное состояние сети (Unusual network condition)
0x50 - 0xFEСбой сетевого адаптера или другого сетевого аппаратного обеспечения

Содержимое поля FinalCCode на выходе Значение
0x00Нет ошибок
0x01Неправильная длина буфера
0x03Неправильный код команды
0x13Неправильный номер имени
0x21Интерфейс занят
0x22Выдано слишком много команд
0x23Неправильное значение в поле номера адаптера AdapterNumber
0x40 - 0x4FНеобычное состояние сети (Unusual network condition)
0x50 - 0xFEСбой сетевого адаптера или другого сетевого аппаратного обеспечения

NB_SendBroadcastDatagram (0xA2)

Команда аналогична предыдущей, за исключением того, что она выполняется без ожидания и в поле Cmd необходимо записать значение 0xA2.

NB_WReceiveDatagram (0x21)

Команда предназначена для приема датаграмм, переданных командой NB_SendDatagram. Она не может принимать датаграммы, переданные в "широковещательном" режиме командой NB_SendBroadcastDatagram. Однако эта команда может принимать датаграммы, посланные на групповое имя.

Если перед вызовом команды в поле NetworkNameNumber блока NCB записать значение 0xFF, команда сможет принимать датаграммы от любой станции для любого имени.

Если длина принятой датаграммы превышает значение, указанное в поле Size, принятый блок данных будет обрезан.

Поля NCB на входе Содержимое
Cmd0x21
NetworkNameNumberНомер, присвоенный при добавлении имени или 0xFF
BufferАдрес буфера, содержащего передаваемые данные
SizeРазмер буфера
PostRoutineДальний указатель на POST-программу или нулевое значение, если POST-программа не используется
AdapterNumberНомер адаптера; 0 - первый адаптер, 1 - второй

Поля NCB на выходе Содержимое
CallNameИмя станции, от которой получена датаграмма
SizeРазмер принятого блока данных
CCodeПромежуточный код завершения команды
FinalCCodeОкончательный код завершения команды

Содержимое поля CCode на выходе Значение
0x00Нет ошибок
0x03Неправильный код команды
0x21Интерфейс занят
0x22Выдано слишком много команд
0x23Неправильное значение в поле номера адаптера AdapterNumber
0x40 - 0x4FНеобычное состояние сети (Unusual network condition)
0x50 - 0xFEСбой сетевого адаптера или другого сетевого аппаратного обеспечения

Содержимое поля FinalCCode на выходе Значение
0x00Нет ошибок
0x01Неправильная длина буфера
0x03Неправильный код команды
0x06Размер буфера слишком мал для того, чтобы разместить в нем принятые данные
0x0BКоманда отменена
0x13Неправильный номер имени
0x17Имя удалено
0x19Конфликт имен (внутренняя ошибка NETBIOS)
0x21Интерфейс занят
0x22Выдано слишком много команд
0x23Неправильное значение в поле номера адаптера AdapterNumber
0x40 - 0x4FНеобычное состояние сети (Unusual network condition)
0x50 - 0xFEСбой сетевого адаптера или другого сетевого аппаратного обеспечения

NB_ReceiveDatagram (0xA1)

Команда аналогична предыдущей, за исключением того, что она выполняется без ожидания и в поле Cmd необходимо записать значение 0xA1.

NB_WReceiveBroadcastDatagram (0x23)

Команда предназначена для приема датаграмм, переданных командой NB_SendBroadcastDatagram. Она не может принимать датаграммы, переданные командой NB_SendDatagram.

Если перед вызовом команды в поле NetworkNameNumber блока NCB записать значение 0xFF, команда сможет принимать датаграммы от любой станции для любого имени.

Если длина принятой датаграммы превышает значение, указанное в поле Size, принятый блок данных будет обрезан.

Поля NCB на входе Содержимое
Cmd0x23
NetworkNameNumberНомер, присвоенный при добавлении имени или 0xFF
BufferАдрес буфера, содержащего передаваемые данные
SizeРазмер буфера
PostRoutineДальний указатель на POST-программу или нулевое значение, если POST-программа не используется
AdapterNumberНомер адаптера; 0 - первый адаптер, 1 - второй

Поля NCB на выходе Содержимое
CallNameИмя станции, от которой получена датаграмма
SizeРазмер принятого блока данных
CCodeПромежуточный код завершения команды
FinalCCodeОкончательный код завершения команды

Содержимое поля CCode на выходе Значение
0x00Нет ошибок
0x03Неправильный код команды
0x21Интерфейс занят
0x22Выдано слишком много команд
0x23Неправильное значение в поле номера адаптера AdapterNumber
0x40 - 0x4FНеобычное состояние сети (Unusual network condition)
0x50 - 0xFEСбой сетевого адаптера или другого сетевого аппаратного обеспечения

Содержимое поля FinalCCode на выходе Значение
0x00Нет ошибок
0x01Неправильная длина буфера
0x03Неправильный код команды
0x06Размер буфера слишком мал для того, чтобы разместить в нем принятые данные
0x0BКоманда отменена
0x13Неправильный номер имени
0x17Имя удалено
0x19Конфликт имен (внутренняя ошибка NETBIOS)
0x21Интерфейс занят
0x22Выдано слишком много команд
0x23Неправильное значение в поле номера адаптера AdapterNumber
0x40 - 0x4FНеобычное состояние сети (Unusual network condition)
0x50 - 0xFEСбой сетевого адаптера или другого сетевого аппаратного обеспечения

NB_ReceiveBroadcastDatagram (0xA3)

Команда аналогична предыдущей, за исключением того, что она выполняется без ожидания и в поле Cmd необходимо записать значение 0xA3.

4.3.3. Работа с каналами

В группе команд, предназначенных для работы с каналами, есть команды для создания канала, команды для удаления канала, а также команда для определения состояния канала.

Канал создается одновременно двумя станциями, одна из которых при этом находится в режиме приема запроса на создание канала, а другая передает такой запрос.

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

Можно создать канал с самим собой, если при создании канала указать в качестве имени партнера свое имя.

NB_WCall (0x10)

Команда устанавливает канал между двумя именами, заданными в блоке NCB. Эти имена могут относиться к программам, работающим на разных станциях или на одной станции. В поле OurName указывается имя станции, которая устанавливает канал, в поле CallName - имя станции, с которой устанавливается канал.

Для успешного создания канала принимающая сторона должна выдать команду NB_Listen, которая будет описана ниже.

Можно установить канал не только с обычным, но и с групповым именем. Для этого придется выдать команду NB_WCall несколько раз, так как за один вызов создается только один канал.

Команда NB_WCall делает несколько попыток создать канал и в случае
неудачи возвращает код ошибки.

При создании канала указывается время тайм-аута для операций приема и передачи данных. Если команды приема или передачи данных через каналы не будут выполнены в течение времени тайм-аута, они (команды) будут прерваны. При этом считается, что канал неработоспособен.

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

Поля NCB на входе Содержимое
Cmd0x10
CallNameИмя, с которым устанавливается канал
OurNameИмя станции, создающей канал
ReceiveTimeoutВремя ожидания приема, в 1/2 с
SendTimeoutВремя ожидания передачи, в 1/2 с
PostRoutineДальний указатель на POST-программу или нулевое значение, если POST-программа не используется
AdapterNumberНомер адаптера; 0 - первый адаптер, 1 - второй

Поля NCB на выходе Содержимое
LocalSessionNumberПрисвоенный номер канала
CCodeПромежуточный код завершения команды
FinalCCodeОкончательный код завершения команды

Содержимое поля CCode на выходе Значение
0x00Нет ошибок
0x03Неправильный код команды
0x09Нет доступных ресурсов
0x15Неправильное имя
0x21Интерфейс занят
0x22Выдано слишком много команд
0x23Неправильное значение в поле номера адаптера AdapterNumber
0x40 - 0x4FНеобычное состояние сети (Unusual network condition)
0x50 - 0xFEСбой сетевого адаптера или другого сетевого аппаратного обеспечения

Содержимое поля FinalCCode на выходе Значение
0x00Нет ошибок
0x03Неправильный код команды
0x05Истекло время ожидания
0x09Нет доступных ресурсов
0x0BКоманда отменена
0x11Переполнилась таблица каналов
0x12Создание канала отвергнуто
0x14Нет ответа от станции с указанным именем или в сети нет такого имени
0x15Неправильное имя
0x18Ненормальное закрытие канала
0x19Конфликт имен (внутренняя ошибка NETBIOS)
0x21Интерфейс занят
0x22Выдано слишком много команд
0x23Неправильное значение в поле номера адаптера AdapterNumber
0x40 - 0x4FНеобычное состояние сети (Unusual network condition)
0x50 - 0xFEСбой сетевого адаптера или другого сетевого аппаратного обеспечения

NB_Call (0x90)

Команда аналогична предыдущей, за исключением того, что она выполняется без ожидания и в поле Cmd необходимо записать значение 0x90.

NB_WListen (0x11)

Команда работает в паре с предыдущей командой и предназначена для организации канала с вызываемой стороны.

В поле CallName блока NCB необходимо указать имя, с которым устанавливается канал. Если в первый байт имени записать символ "*", канал будет установлен с любой вызывающей станцией.

Программа может выдать несколько команд NB_Listen для создания одно-
временно нескольких каналов.

В случае успешного завершения команда запишет в поле LocalSessionNumber номер созданного канала.

При создании канала необходимо указать время тайм-аута для операций приема и передачи данных через канал. Сама команда NB_WListen не использует тайм-аут. Программа, выдавшая эту команду, будет находиться в состоянии ожидания до тех пор, пока какая-либо станция не пожелает создать с ней канал. Для исключения состояния "зависания" программы лучше использовать вариант NB_Listen этой команды (без ожидания).

Поля NCB на входе Содержимое
Cmd0x11
CallNameИмя, с которым устанавливается канал. Если в первый байт имени записать символ "*", канал будет установлен с любой вызывающей станцией
OurNameИмя станции, создающей канал с принимающей стороны
ReceiveTimeoutВремя ожидания приема, в 1/2 с
SendTimeoutВремя ожидания передачи, в 1/2 с
PostRoutineДальний указатель на POST-программу или нулевое значение, если POST-программа не используется
AdapterNumberНомер адаптера; 0 - первый адаптер, 1 - второй

   

Поля NCB на выходе Содержимое
LocalSessionNumberПрисвоенный номер канала
CallNameВ это поле записывается имя вызвавшей станции, если при установлении канала было указано имя "*"
CCodeПромежуточный код завершения команды
FinalCCodeОкончательный код завершения команды

Содержимое поля CCode на выходе Значение
0x00Нет ошибок
0x03Неправильный код команды
0x09Нет доступных ресурсов
0x15Неправильное имя
0x21Интерфейс занят
0x22Выдано слишком много команд
0x23Неправильное значение в поле номера адаптера AdapterNumber
0x40 - 0x4FНеобычное состояние сети (Unusual network condition)
0x50 - 0xFEСбой сетевого адаптера или другого сетевого аппаратного обеспечения

Содержимое поля FinalCCode на выходе Значение
0x00Нет ошибок
0x03Неправильный код команды
0x09Нет доступных ресурсов
0x0BКоманда отменена
0x11Переполнилась таблица каналов
0x15Неправильное имя
0x17Указанное имя было удалено
0x18Ненормальное закрытие канала
0x19Конфликт имен (внутренняя ошибка NETBIOS)
0x21Интерфейс занят
0x22Выдано слишком много команд
0x23Неправильное значение в поле номера адаптера AdapterNumber
0x40 - 0x4FНеобычное состояние сети (Unusual network condition)
0x50 - 0xFEСбой сетевого адаптера или другого сетевого аппаратного обеспечения

NB_Listen (0x91)

Команда аналогична предыдущей, за исключением того, что она выполняется без ожидания и в поле Cmd необходимо записать значение 0x91.

NB_WHangUp (0x12)

Команда предназначена для закрытия канала, номер которого указан в поле LocalSessionNumber блока NCB. Она должна быть выполнена с обеих сторон канала после завершения работы.

Если для закрываемого с помощью этой команды канала на других станциях выдана команда NB_WReceive, она завершается с кодом ощибки 0x0A. Выданная для закрываемого канала команда NB_WSend также завершается с кодом 0x0A, но через 20 секунд, которые отводятся ей для завершения своей работы. Если команда NB_WSend не успела завершить передачу за 20 секунд, команда NB_WHangUp завершается с кодом 0x05.

Аналогично с кодом 0x0A завершает свою работу и команда NB_WReceiveAny, выданная для закрываемого канала.

Другие команды, выданные для закрываемого канала, завершаются с кодом 0x18.

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

Поля NCB на входе Содержимое
Cmd0x12
LocalSessionNumberНомер закрываемого канала
PostRoutineДальний указатель на POST-программу или нулевое значение, если POST-программа не используется
AdapterNumberНомер адаптера; 0 - первый адаптер, 1 - второй

Поля NCB на выходе Содержимое
CCodeПромежуточный код завершения команды
FinalCCodeОкончательный код завершения команды

Содержимое поля CCode на выходе Значение
0x00Нет ошибок
0x03Неправильный код команды
0x21Интерфейс занят
0x22Выдано слишком много команд
0x23Неправильное значение в поле номера адаптера AdapterNumber
0x40 - 0x4FНеобычное состояние сети (Unusual network condition)
0x50 - 0xFEСбой сетевого адаптера или другого сетевого аппаратного обеспечения

Содержимое поля FinalCCode на выходе Значение
0x00Нет ошибок
0x03Неправильный код команды
0x05Истекло время ожидания
0x08Неправильный номер канала
0x0AКанал уже закрыт
0x0BКоманда отменена
0x18Ненормальное закрытие канала
0x19Конфликт имен (внутренняя ошибка NETBIOS)
0x21Интерфейс занят
0x22Выдано слишком много команд
0x23Неправильное значение в поле номера адаптера AdapterNumber
0x40 - 0x4FНеобычное состояние сети (Unusual network condition)
0x50 - 0xFEСбой сетевого адаптера или другого сетевого аппаратного обеспечения

NB_HangUp (0x92)

Команда аналогична предыдущей, за исключением того, что она выполняется без ожидания и в поле Cmd необходимо записать значение 0x92.

NB_WSessionStatus (0x34)

Команда возвращает программе состояние канала, имя которого указано в поле OurName блока NCB. В качестве имени можно указать символ "*", в этом случае программа получит информацию о каналах, относящихся ко всем именам станций, имеющихся в локальной таблице имен на вызывающей команду станции.

Информация о состоянии каналов возвращается в буфер, адрес которого программа должна записать в поле Buffer блока NCB. Размер буфера должен быть указан в поле Size блока NCB.

Формат буфера можно описать следующей структурой:

struct _SESSION_STATUS {
        unsigned char NameNumber;
        unsigned char SessionCount;
        unsigned char DatagramsOutstanding;
        unsigned char ReceiveAnyoutstanding;
        struct _SESSION {
                unsigned char LocalSessionNumber;
                unsigned char State;
                char LocalName[16];
                char RemoteName[16];
                unsigned char ReceiveCount;
                unsigned char SendCount;
        } Session[40];
};

Приведем список полей буфера:

Название поля Назначение поля
NameNumberНомер имени канала
SessionCountКоличество каналов
DatagramsOutstandingКоличество выданных команд на прием датаграмм
ReceiveAnyoutstandingКоличество выданных команд на прием командой NB_ReceiveAny
SessionМассив структур, описывающих каждый канал в отдельности
LocalSessionNumberНомер канала
StateСостояние канала:
1 - ожидание завершения команды NB_Listen;
2 - ожидание завершения команды NB_Call;
3 - канал установлен;
4 - ожидание завершения команды NB_HangUp;
5 - команда NB_HangUp завершила свое вы- полнение;
6 - канал закрыт с ошибкой.
LocalNameИмя локальной станции
RemoteNameИмя удаленной станции
ReceiveCountКоличество ожидающих завершение команд NB_Receive
SendCountКоличество ожидающих завершения команд NB_Send

Поля NCB на входе Содержимое
Cmd0x34
BufferДальний адрес буфера для приема информации
о состоянии каналов
SizeРазмер буфера
OurNamesИмя канала, для которого необходимо получить информацию о состоянии. В качестве имени можно указывать "*"
PostRoutineДальний указатель на POST-программу или нулевое значение, если POST-программа не используется
AdapterNumberНомер адаптера; 0 - первый адаптер, 1 - второй

Поля NCB на выходе Содержимое
SizeРазмер заполненной части буфера
CCodeПромежуточный код завершения команды
FinalCCodeОкончательный код завершения команды

Содержимое поля CCode на выходе Значение
0x00Нет ошибок
0x03Неправильный код команды
0x15Неправильное имя
0x21Интерфейс занят
0x22Выдано слишком много команд
0x23Неправильное значение в поле номера адаптера AdapterNumber
0x40 - 0x4FНеобычное состояние сети (Unusual network condition)
0x50 - 0xFEСбой сетевого адаптера или другого сетевого аппаратного обеспечения
Содержимое поля FinalCCode на выходе Значение
0x00Нет ошибок
0x01Неправильная длина буфера
0x03Неправильный код команды
0x06Слишком мал размер выделенного буфера
0x19Конфликт имен (внутренняя ошибка NETBIOS)
0x21Интерфейс занят
0x22Выдано слишком много команд
0x23Неправильное значение в поле номера адаптера AdapterNumber
0x40 - 0x4FНеобычное состояние сети (Unusual network condition)
0x50 - 0xFEСбой сетевого адаптера или другого сетевого аппаратного обеспечения

NB_SessionStatus (0xB4)

Команда аналогична предыдущей, за исключением того, что она выполняется без ожидания и в поле Cmd необходимо записать значение 0xB4.

4.3.4. Прием и передача данных через каналы

NB_WSend (0x14)

С помощью этой команды программа может передать блок данных размером от 1 до 65535 байт по созданному ранее каналу. Перед вызовом команды программа должна записать номер канала, по которому будет выполняться передача, в поле LocalSessionNumber блока NCB. Адрес передаваемого блока данных и его длина должны быть записаны в поля Buffer и Size.

Для приема данных, передаваемых командой NB_WSend, необходимо использовать команду NB_WReceive (или NB_Receive).

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

Если истекло время тайм-аута, заданного при создании канала, команда завершается с ошибкой.

Поля NCB на входе Содержимое
Cmd0x14
LocalSessionNumber Номер используемого канала
BufferУказатель на буфер, содержащий передаваемые данные
SizeРазмер буфера
PostRoutineДальний указатель на POST-программу или нулевое значение, если POST-программа не используется
AdapterNumberНомер адаптера; 0 - первый адаптер, 1 - второй
Поля NCB на выходе Содержимое
CCodeПромежуточный код завершения команды
FinalCCodeОкончательный код завершения команды

Содержимое поля CCode на выходе Значение
0x00Нет ошибок
0x03Неправильный код команды
0x21Интерфейс занят
0x22Выдано слишком много команд
0x23Неправильное значение в поле номера адаптера AdapterNumber
0x40 - 0x4FНеобычное состояние сети (Unusual network condition)
0x50 - 0xFEСбой сетевого адаптера или другого сетевого аппаратного обеспечения

Содержимое поля FinalCCode на выходе Значение
0x00Нет ошибок
0x03Неправильный код команды
0x05Истекло время ожидания
0x08Неправильный номер канала
0x0AКанал закрыт
0x0BКоманда отменена
0x18Ненормальное закрытие канала
0x21Интерфейс занят
0x22Выдано слишком много команд
0x23Неправильное значение в поле номера адаптера AdapterNumber
0x40 - 0x4FНеобычное состояние сети (Unusual network condition)
0x50 - 0xFEСбой сетевого адаптера или другого сетевого аппаратного обеспечения

NB_Send (0x94)

Команда аналогична предыдущей, за исключением того, что она выполняется без ожидания и в поле Cmd необходимо записать значение 0x94.

NB_WSendNoAck (0x71)

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

Поля NCB на входе Содержимое
Cmd0x71
LocalSessionNumber Номер используемого канала
BufferУказатель на буфер, содержащий передаваемые данные
SizeРазмер буфера
PostRoutineДальний указатель на POST-программу или нулевое значение, если POST-программа не используется
AdapterNumberНомер адаптера; 0 - первый адаптер, 1 - второй

Поля NCB на выходе Содержимое
CCodeПромежуточный код завершения команды
FinalCCodeОкончательный код завершения команды

Содержимое поля CCode на выходе Значение
0x00Нет ошибок
0x03Неправильный код команды
0x21Интерфейс занят
0x22Выдано слишком много команд
0x23Неправильное значение в поле номера адаптера AdapterNumber
0x40 - 0x4FНеобычное состояние сети (Unusual network condition)
0x50 - 0xFEСбой сетевого адаптера или другого сетевого аппаратного обеспечения

Содержимое поля FinalCCode на выходе Значение
0x00Нет ошибок
0x03Неправильный код команды
0x05Истекло время ожидания
0x08Неправильный номер канала
0x0AКанал закрыт
0x0BКоманда отменена
0x18Ненормальное закрытие канала
0x21Интерфейс занят
0x22Выдано слишком много команд
0x23Неправильное значение в поле номера адаптера AdapterNumber
0x40 - 0x4FНеобычное состояние сети (Unusual network condition)
0x50 - 0xFEСбой сетевого адаптера или другого сетевого аппаратного обеспечения

NB_SendNoAck (0xF1)

Команда аналогична предыдущей, за исключением того, что она выполняется без ожидания и в поле Cmd необходимо записать значение 0xF1.

NB_WChainSend (0x17)

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

Первый буфер задается, как и для команды NB_WSend, через поля Buffer и Size. Размер второго буфера должен быть записан в первые два байта поля CallName блока ECB, а его адрес занимает следующие четыре байта этого поля.

Поля NCB на входе Содержимое
Cmd0x17
LocalSessionNumber Номер используемого канала
BufferУказатель на первый буфер, содержащий передаваемые данные
SizeРазмер первого буфера
CallNameПервые два байта содержат размер второго буфера, следующие четыре байта - дальний адрес второго буфера
PostRoutineДальний указатель на POST-программу или нулевое значение, если POST-программа не используется
AdapterNumberНомер адаптера; 0 - первый адаптер, 1 - второй

Поля NCB на выходе Содержимое
CCodeПромежуточный код завершения команды
FinalCCodeОкончательный код завершения команды

Содержимое поля CCode на выходе Значение
0x00Нет ошибок
0x03Неправильный код команды
0x21Интерфейс занят
0x22Выдано слишком много команд
0x23Неправильное значение в поле номера адаптера AdapterNumber
0x40 - 0x4FНеобычное состояние сети (Unusual network condition)
0x50 - 0xFEСбой сетевого адаптера или другого сетевого аппаратного обеспечения

Содержимое поля FinalCCode на выходе Значение
0x00Нет ошибок
0x03Неправильный код команды
0x05Истекло время ожидания
0x08Неправильный номер канала
0x0AКанал закрыт
0x0BКоманда отменена
0x18Ненормальное закрытие канала
0x21Интерфейс занят
0x22Выдано слишком много команд
0x23Неправильное значение в поле номера адаптера AdapterNumber
0x40 - 0x4FНеобычное состояние сети (Unusual network condition)
0x50 - 0xFEСбой сетевого адаптера или другого сетевого аппаратного обеспечения

NB_ChainSend (0x97)

Команда аналогична предыдущей, за исключением того, что она выполняется без ожидания и в поле Cmd необходимо записать значение 0x97.

NB_WChainSendNoAck (0x72)

Команда аналогична команде NB_WCainSend, однако в отличие от нее не выполняет проверку доставки блока данных принимающей стороне. За счет этого она работает немного быстрее.

Поля NCB на входе Содержимое
Cmd0x72
LocalSessionNumber Номер используемого канала
BufferУказатель на первый буфер, содержащий передаваемые данные
SizeРазмер первого буфера
CallNameПервые два байта содержат размер второго буфера, следующие четыре байта - дальний адрес второго буфера
PostRoutineДальний указатель на POST-программу или нулевое значение, если POST-программа не используется
AdapterNumberНомер адаптера; 0 - первый адаптер, 1 - второй

Поля NCB на выходе Содержимое
CCodeПромежуточный код завершения команды
FinalCCodeОкончательный код завершения команды

Содержимое поля CCode на выходе Значение
0x00Нет ошибок
0x03Неправильный код команды
0x21Интерфейс занят
0x22Выдано слишком много команд
0x23Неправильное значение в поле номера адаптера AdapterNumber
0x40 - 0x4FНеобычное состояние сети (Unusual network condition)
0x50 - 0xFEСбой сетевого адаптера или другого сетевого аппаратного обеспечения

Содержимое поля FinalCCode на выходе Значение
0x00Нет ошибок
0x03Неправильный код команды
0x05Истекло время ожидания
0x08Неправильный номер канала
0x0AКанал закрыт
0x0BКоманда отменена
0x18Ненормальное закрытие канала
0x21Интерфейс занят
0x22Выдано слишком много команд
0x23Неправильное значение в поле номера адаптера AdapterNumber
0x40 - 0x4FНеобычное состояние сети (Unusual network condition)
0x50 - 0xFEСбой сетевого адаптера или другого сетевого аппаратного обеспечения

NB_ChainSendNoAck (0xF2)

Команда аналогична предыдущей, за исключением того, что она выполняется без ожидания и в поле Cmd необходимо записать значение 0xF2.

NB_WReceive (0x15)

Команда принимает данные, посланные командами NB_WSend или NB_WChainSend.

Если размер буфера недостаточен для записи принятых данных, команда возвращает код ошибки 0x06; в этом случае вы можете вызвать команду еще раз для того, чтобы прочесть данные, не поместившиеся в буфере при предыдущем вызове команды.

Поля NCB на входе Содержимое
Cmd0x15
LocalSessionNumber Номер используемого канала
BufferУказатель на буфер, используемый для записи принятых данных
SizeРазмер буфера
PostRoutineДальний указатель на POST-программу или нулевое значение, если POST-программа не используется
AdapterNumberНомер адаптера; 0 - первый адаптер, 1 - второй

Поля NCB на выходе Содержимое
SizeКоличество принятых и записанных в буфер байт данных
CCodeПромежуточный код завершения команды
FinalCCodeОкончательный код завершения команды

Содержимое поля CCode на выходе Значение
0x00Нет ошибок
0x03Неправильный код команды
0x21Интерфейс занят
0x22Выдано слишком много команд
0x23Неправильное значение в поле номера адаптера AdapterNumber
0x40 - 0x4FНеобычное состояние сети (Unusual network condition)
0x50 - 0xFEСбой сетевого адаптера или другого сетевого аппаратного обеспечения

Содержимое поля FinalCCode на выходе Значение
0x00Нет ошибок
0x03Неправильный код команды
0x05Истекло время ожидания
0x06Размер буфера недостаточен для записи принятых данных
0x08Неправильный номер канала
0x0AКанал закрыт
0x0BКоманда отменена
0x18Ненормальное закрытие канала
0x21Интерфейс занят
0x22Выдано слишком много команд
0x23Неправильное значение в поле номера адаптера AdapterNumber
0x40 - 0x4FНеобычное состояние сети (Unusual network condition)
0x50 - 0xFEСбой сетевого адаптера или другого сетевого аппаратного обеспечения

NB_Receive (0x95)

Команда аналогична предыдущей, за исключением того, что она выполняется без ожидания и в поле Cmd необходимо записать значение 0x95.

NB_WReceiveAny (0x16)

Команда принимает данные от всех партнеров. Для этой команды вместо номера канала необходимо указать номер имени, полученный вашей программой при добавлении имени. Если в поле NetworkNameNumber проставить значение 0xFF, эта команда будет принимать данные от любых партнеров для любых каналов, созданных на вашей станции.

Если размер буфера недостаточен для записи принятых данных, команда возвращает код ошибки 0x06; в этом случае вы можете вызвать команду еще раз для того, чтобы прочесть данные, не поместившиеся в буфере при предыдущем вызове команды.

Поля NCB на входе Содержимое
Cmd0x16
NetworkNameNumber Номер имени или 0xFF
BufferУказатель на буфер, используемый для записи принятых данных
SizeРазмер буфера
PostRoutineДальний указатель на POST-программу или нулевое значение, если POST-программа не используется
AdapterNumberНомер адаптера; 0 - первый адаптер, 1 - второй

Поля NCB на выходе Содержимое
SizeКоличество принятых и записанных в буфер байт данных
NetworkNameNumber Номер имени станции, от которой пришли данные
CCodeПромежуточный код завершения команды
FinalCCodeОкончательный код завершения команды

Содержимое поля CCode на выходе Значение
0x00Нет ошибок
0x03Неправильный код команды
0x21Интерфейс занят
0x22Выдано слишком много команд
0x23Неправильное значение в поле номера адаптера AdapterNumber
0x40 - 0x4FНеобычное состояние сети (Unusual network condition)
0x50 - 0xFEСбой сетевого адаптера или другого сетевого аппаратного обеспечения

Содержимое поля FinalCCode на выходе Значение
0x00Нет ошибок
0x03Неправильный код команды
0x06Размер буфера недостаточен для записи принятых данных
0x08Неправильный номер канала
0x0AКанал закрыт
0x0BКоманда отменена
0x13Неправильный номер имени
0x17Указанное имя было удалено
0x18Ненормальное закрытие канала
0x19Конфликт имен (внутренняя ошибка NETBIOS)
0x21Интерфейс занят
0x22Выдано слишком много команд
0x23Неправильное значение в поле номера адаптера AdapterNumber
0x40 - 0x4FНеобычное состояние сети (Unusual network condition)
0x50 - 0xFEСбой сетевого адаптера или другого сетевого аппаратного обеспечения

NB_ReceiveAny (0x96)

Команда аналогична предыдущей, за исключением того, что она выполняется без ожидания и в поле Cmd необходимо записать значение 0x96.

4.3.5. Другие команды

В этом разделе мы опишем команды NETBIOS, позволяющие сбросить драйвер NETBIOS в исходное состояние, отменить выданную ранее команду.

NB_WResetAdapter (0x32)

Команда сбрасывает NETBIOS в исходное состояние, удаляет все имеющиеся каналы и имена (кроме постоянного имени, которое нельзя удалить, не вытащив сетевой адаптер из компьютера). С помощью этой команды можно также изменить максимальное количество доступных программе каналов и используемых одновременно блоков NCB. По умолчанию доступны шесть каналов и 12 блоков NCB.

Поля NCB на входе Содержимое
Cmd0x32
LocalSessionNumber Максимальное количество каналов или 0 для использования значения по умолчанию
NetworkNameNumber Максимальное количество блоков NCB или 0 для использования значения по умолчанию
AdapterNumberНомер адаптера; 0 - первый адаптер, 1 - второй

Поля NCB на выходе Содержимое
FinalCCodeОкончательный код завершения команды

Содержимое поля CCode на выходеНе используется

Содержимое поля FinalCCode на выходе Значение
0x00Нет ошибок
0x03Неправильный код команды
0x23Неправильное значение в поле номера адаптера AdapterNumber
0x40 - 0x4FНеобычное состояние сети (Unusual network condition)
0x50 - 0xFEСбой сетевого адаптера или другого сетевого аппаратного обеспечения

NB_WCancel (0x35)

Команда используется для отмены других запущенных команд. Адрес NCB для отменяемой команды должен быть записан в поле Buffer.

С помощью этой команды нельзя отменить следующие команды: NB_AddName, NB_AddGroupName, NB_DeleteName, NB_SendDatagramm, NB_SendBroadcastDatagramm, NB_ResetAdapter, NB_SessionStatus, NB_Cancel, NB_Unlink.

Поля NCB на входе Содержимое
Cmd0x35
BufferУказатель на блок NCB, для которого отменяется команда
AdapterNumberНомер адаптера; 0 - первый адаптер, 1 - второй

Поля NCB на выходе Содержимое
FinalCCodeОкончательный код завершения команды

Содержимое поля CCode на выходеНе используется

Содержимое поля FinalCCode на выходе Значение
0x00Нет ошибок
0x03Неправильный код команды
0x23Неправильное значение в поле номера адаптера AdapterNumber
0x24Команда уже начала выполняться, когда пришел запрос на ее отмену
0x26Данную команду нельзя отменить
0x40 - 0x4FНеобычное состояние сети (Unusual network condition)
0x50 - 0xFEСбой сетевого адаптера или другого сетевого аппаратного обеспечения

4.4. Коды ошибок

Приведем список кодов ошибок, возвращаемых NETBIOS:

Код ошибкиЗначение
00Команда выполнена без ошибок
01Неправильная длина буфера.
Возможно, что при выполнении операции передачи датаграммы указан размер буфера более 512 байт
03Неправильная команда
05Истекло заданное время ожидания.
Истекло время ожидания для команды NB_Call. Другая возможная причина - истекло время ожидания, заданное при создании канала для команд приема или передачи данных по каналу
06Данные приняты не полностью.
Указанный размер буфера недостаточен для размещения принятых данных
07Данные, переданные в режиме без подтверждения, не были приняты
08Неправильный номер канала.
Канал с указанным номером не существует
09Нет доступных ресурсов
0AКанал закрыт
0BКоманда отменена
0DТакое имя уже есть в локальной таблице имен
0EПереполнение локальной таблицы имен.
Задано более 16 имен
0FУказанное имя используется активным каналом
11Переполнение локальной таблицы каналов
12Создание канала отвергнуто.
Это означает, что на удаленном компьютере не выполняется команда NB_Listen, предназначенная для создания канала
13Неправильный номер адаптера
14NETBIOS не может найти вызываемое имя
15Имя не найдено или задано неправильно
16Имя уже используется удаленной станцией
17Имя удалено
18Ненормальное завершение работы канала.
Либо удаленный компьютер был выключен, либо возникли не-
исправности в его сетевом оборудовании, либо были прерваны или отменены выполнявшиеся там команды передачи данных по каналу
19Конфликт имени.
Драйвер NETBIOS обнаружил присутствие в сети двух одинаковых имен, что недопустимо
1AПринят нестандартный пакет, не соответствующий протоколу NETBIOS
21Интерфейс занят.
Эта ошибка появляется при попытке вызвать команду NETBIOS из POST-программы или обработчика прерываний
22Слишком много выдано команд
23Неправильный номер сетевого адаптера.
Для номера адаптера можно использовать значения 00h (первый адаптер) или 01h (второй адаптер)
24Команда уже начала выполняться, когда пришел запрос на ее отмену
26Команда не может быть отменена
30Имя используется другим окружением
34Среда не определена, необходимо выдать команду NB_Reset
35Ресурс занят, необходимо отложить запрос
36Превышено максимальное число работающих приложений
38Запрошенный ресурс недоступен
39Неправильный адрес блока NCB
3AКоманда NB_Reset не может выдаваться из POST-программы
3CNETBIOS не смог заблокировать память программы пользователя
3FОшибка при открытии драйвера сетевого адаптера
4E - 4FОшибка в состоянии сети
F6 - FAОшибка адаптера
FBДрайвер NETBIOS не загружен
FCОшибка при открытии сетевого адаптера
FDНеожиданное закрытие сетевого адаптера
FENETBIOS не активен

4.5. Система "клиент-сервер" на базе датаграмм

Приведем пример простейшей системы "клиент-сервер", в которую входят две программы, обменивающиеся датаграммами (листинг 18).

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

При использовании протокола NETBIOS ситуация сильно упрощается. Вполне достаточно, если программа-клиент будет знать имя, которое добавляется сервером и используется для приема пакетов от клиентов.

Программа-сервер начинает свою работу с создания объекта класса NETBIOS_DATAGRAM_SERVER. Конструктор в качестве параметра получает имя, добавляемое сервером и используемое для приема пакетов. В нашем случае это имя "NETBIOS Server".

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

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

// ===================================================
// Листинг 18. Сервер 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_DATAGRAM_SERVER {

        unsigned errno;
        void interrupt ( *int5C)(...);

public:

// Здесь хранится имя сервера и номер этого имени

        char OurName[16];
        unsigned NetworkNameNumber;

// Конструктор, проверяет наличие NETBIOS и добавляет имя

        NETBIOS_DATAGRAM_SERVER(char *Name) {

// Блок NCB, который будет использован при добавлении имени
// NCB AddNameNCB;

// Проверяем длину имени 

                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();
        }

// Деструктор, удаляет имя.

        ~NETBIOS_DATAGRAM_SERVER() {
                NCB AddNameNCB;
                AddNameNCB.WDeleteName(OurName);
                errno = AddNameNCB.Error();
        }

// Функция для проверки кода ошибки

        int Error(void) {return errno;}

// Функция для приема датаграммы

        void Receive(char *ReceiveBuffer, unsigned BufferSize) {
                NCB ReceiveNCB;

// Записываем в NCB адрес и длину буфера

                ReceiveNCB.SetBuffer(ReceiveBuffer, BufferSize);

// Выполняем прием датаграммы с ожиданием

                ReceiveNCB.WReceiveDatagram(NetworkNameNumber);

        }
};

void main(void) {

// Наш сервер с именем "NETBIOS Server"

        NETBIOS_DATAGRAM_SERVER Server("NETBIOS Server");
        char ReceiveBuffer[512];

// Проверяем, были ли ошибки на этапе инициализации сервера.

        if(Server.Error()) {
                printf("Ошибка %02.2X\n", Server.Error());
                return;
        }
        printf("Инициализация завершена.\n");
        printf("Ожидаем сообщение от клиента.\n");

// Принимаем сообщение от клиента

        Server.Receive(ReceiveBuffer, 512);
        printf("Принято: >%s<\n",ReceiveBuffer);

}

Программа-сервер работает в паре с программой-клиентом (листинг 19).

После запуска программа-клиент создает объект класса NETBIOS_DATAGRAM_CLIENT. Конструктор и деструктор этого класса выполняют действия, аналогичные конструктору и деструктору класса NETBIOS_DATAGRAM_SERVER.

После инициализации и проверки ошибок программа-клиент посылает сообщение "Привет от клиента NETBIOS!" серверу с именем "NETBIOS Server" при помощи функции Send(). Затем программа-клиент завершает свою работу.

// ===================================================
// Листинг 19. Клиент 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_DATAGRAM_CLIENT {

        unsigned errno;

public:

// Здесь хранится имя клиента и номер этого имени

        char OurName[16];
        unsigned NetworkNameNumber;
        union REGS regs;

// Конструктор, проверяет наличие NETBIOS и добавляет имя

        NETBIOS_DATAGRAM_CLIENT(char *Name) {

// Блок NCB, который будет использован при добавлении имени

                NCB AddNameNCB;

// Проверяем длину имени имя

                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();
        }

// Деструктор, удаляет имя.

        ~NETBIOS_DATAGRAM_CLIENT() {
                NCB AddNameNCB;
                AddNameNCB.WDeleteName(OurName);
                errno = AddNameNCB.Error();
        }

// Функция для проверки кода ошибки

        int Error(void) {return errno;}

// Функция для приема датаграммы

        void Receive(char *ReceiveBuffer, unsigned BufferSize) {
                NCB ReceiveNCB;

// Записываем в NCB адрес и длину буфера

                ReceiveNCB.SetBuffer(ReceiveBuffer, BufferSize);

// Выполняем прием датаграммы с ожиданием

                ReceiveNCB.WReceiveDatagram(NetworkNameNumber);

        }

// Функция для передачи датаграммы

        void Send(char *ReceiveBuffer, unsigned BufferSize,
                                        char *CallName) {
                NCB SendNCB;

// Устанавливаем адрес и длину буфера

                SendNCB.SetBuffer(ReceiveBuffer, BufferSize);

// Устанавливаем имя партнера, которому будет передана
// наша датаграмма

                SendNCB.SetCallName(CallName);

// Передаем датаграмму с ожиданием

                SendNCB.WSendDatagram(NetworkNameNumber);
        }
};

void main(void) {

// Наш клиент с именем "NETBIOS Client"

        NETBIOS_DATAGRAM_CLIENT Client("NETBIOS Client");

// Проверяем, были ли ошибки на этапе инициализации клиента.

        if(Client.Error()) {
                printf("Ошибка %02.2X\n", Client.Error());
                return;
        }
        printf("Инициализация завершена.\n");

// Передаем сообщение серверу с именем "NETBIOS Server"

        Client.Send("Привет от клиента NETBIOS!", 512, "NETBIOS                                         Server");

}

В include-файле netbios.hpp (листинг 20) приведены определения констант и классов для работы с протоколом NETBIOS через прерывание INT 5Ch.

В классе NCB, кроме структуры данных _NCB, определены конструктор NCB() и несколько других функций для работы с этим классом.

Конструктор расписывает структуру ncb нулями и сбрасывает код ошибки в переменной errno.

Функция NetBios() вызывает прерывание NETBIOS.

Функции WAddName() и WDeleteName() определены в файле nbfunc.cpp (листинг 21). Они предназначены для добавления и удаления имени.

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

// ===================================================
// Листинг 20. Классы для работы с NETBIOS
//
// Файл netbios.hpp
//
// (C) A. Frolov, 1993
// ===================================================

// Команды NETBIOS

// Команды для работы с именами

#define NB_WAddName 0x30
#define NB_AddName 0xb0
#define NB_WAddGroupName 0x36
#define NB_AddGroupName 0xb6
#define NB_WDeleteName 0x31
#define NB_DeleteName 0xb1

// Команды для передачи датаграмм

#define NB_WSendDatagram 0x20
#define NB_SendDatagram 0xa0
#define NB_WSendBroadcastDatagram 0x22
#define NB_SendBroadcastDatagram 0xa2

// Команды для приема датаграмм

#define NB_WReceiveDatagram 0x21
#define NB_ReceiveDatagram 0xa1
#define NB_WReceiveBroadcastDatagram 0x23
#define NB_ReceiveBroadcastDatagram 0xa3

// Команды для работы с каналами

#define NB_WCall 0x10
#define NB_Call 0x90
#define NB_WListen 0x11
#define NB_Listen 0x91
#define NB_WHangUp 0x12
#define NB_HangUp 0x92

// Команды для передачи данных по каналу

#define NB_WSend 0x14
#define NB_Send 0x94
#define NB_WSendNoAck 0x71
#define NB_SendNoAck 0xf1

#define NB_WChainSend 0x17
#define NB_ChainSend 0x97
#define NB_WChainSendNoAck 0x72
#define NB_ChainSendNoAck 0xf2
// Команды для приема данных по каналу

#define NB_WReceive 0x15
#define NB_Receive 0x95
#define NB_WReceiveAny 0x16
#define NB_ReceiveAny 0x96

// Прочие команды

#define NB_WResetAdapter 0x32
#define NB_WCancel 0x35
#define NB_WSessionStatus 0x34
#define NB_SessionStatus 0xb4

// Класс NCB для работы с командами NETBIOS

class NCB {

// Стандартный блок NCB в формате NETBIOS

        struct _NCB {
                unsigned char Cmd;
                unsigned char CCode;
                unsigned char LocalSessionNumber;
                unsigned char NetworkNameNumber;
                void far *Buffer;
                unsigned Size;
                char CallName[16];
                char OurName[16];
                unsigned char ReceiveTimeout;
                unsigned char SendTimeout;
                void interrupt (*PostRoutine)(void);
                unsigned char AdapterNumber;
                unsigned char FinalCCode;
                unsigned char Reserved[14];
        } ncb;
        struct SREGS sregs;
        union REGS regs;
        unsigned errno;

// Функция для вызова NETBIOS

        void NetBios(void) {
                sregs.es = FP_SEG(&ncb);
                regs.x.bx = FP_OFF(&ncb);
                int86x(0x5c, &regs, &regs, &sregs);
        }

public:

// Конструктор, расписывает ncb нулями

        NCB() {
                memset(&ncb, 0, sizeof(ncb));
                errno = 0;
        }

// Функция возвращает код ошибки

        int Error(void) {return errno;}

// Функция для добавления имени

        void WAddName(char *name);

// Функция для удаления имени

        void WDeleteName(char *name);

// Функция для определения номера имени

        unsigned GetNetworkNameNumber(void)
                { return(ncb.NetworkNameNumber); }

// Функция для установки адреса и размера буфера

        void SetBuffer(char far *Buf,   unsigned BufSize) {
                ncb.Buffer = Buf;
                ncb.Size = BufSize;
        }

// Установка в ncb имени вызываемого партнера

        void SetCallName(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();
        }

};

В файле nbfunc.cpp приведены определения некоторых функций из класса NCB (листинг 21):

// ===================================================
// Листинг 21. Функции для NETBIOS
//
// Файл nbfunc.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"

// Добавляем имя

        void NCB::WAddName(char *name) {
                char buf[16];

// Проверяем длину имени

                if(strlen(name) > 15) {
                        errno = 0xff;
                        return;
                }
                strcpy(buf, name);

// При необходимости дополняем имя пробелами

                while (strlen(buf) < 15) strcat(buf, " ");

// Вызываем NETBIOS

                ncb.Cmd = NB_WAddName;
                strcpy(ncb.OurName, buf);
                NetBios();
                errno = ncb.FinalCCode;
        }

// Удаление имени

        void NCB::WDeleteName(char *name) {
                char buf[16];

// Проверяем длину имени

                if(strlen(name) > 15) {
                        errno = 0xff;
                        return;
                }
                strcpy(buf, name);

// При необходимости дополняем имя пробелами

                while (strlen(buf) < 15) strcat(buf, " ");

                strcpy(ncb.OurName, buf);

// Вызываем NETBIOS

                ncb.Cmd = NB_WDeleteName;
                NetBios();
                errno = ncb.FinalCCode;
        }

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

        void NCB::SetCallName(char *name) {
                char buf[16];
                if(strlen(name) > 15) {
                        errno = 0xff;
                        return;
                }
                strcpy(buf, name);
                while (strlen(buf) < 15) strcat(buf, " ");
                strcpy(ncb.CallName, buf);
        }

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, &regs, &regs, &sregs);
        }

public:

// Конструктор, расписывает ncb нулями

        NCB() {
                memset(&ncb, 0, sizeof(ncb));
                errno = 0;
        }

// Функция возвращает код ошибки

        int Error(void) {return errno;}

// Функция для добавления имени

        void WAddName(char *name);

// Функция для удаления имени

        void WDeleteName(char *name);

// Функция для определения номера имени

        unsigned GetNetworkNameNumber(void)
                { return(ncb.NetworkNameNumber); }

// Функция для установки адреса и размера буфера

        void SetBuffer(char far *Buf,   unsigned BufSize) {
                ncb.Buffer = Buf;
                ncb.Size = BufSize;
        }

// Установка в NCB тайм-аута

        void SetRtoSto(int rto, int sto) {
                ncb.ReceiveTimeout = rto;
                ncb.SendTimeout = sto;
        }

// Установка в ncb имени вызываемого партнера

        void SetCallName(char *name);

// Установка в ncb имени нашей станции

        void SetOurName(char *name);

// Прием датаграмм с ожиданием

        void WReceiveDatagram(int NetwrkNameNumber) {

// Заполняем поле номера своего имени

                ncb.NetworkNameNumber = NetwrkNameNumber;

// Вызываем NETBIOS

                ncb.Cmd = NB_WReceiveDatagram;
                NetBios();
        }

// Передача датаграмм с ожиданием

        void WSendDatagram(int NetwrkNameNumber) {

// Заполняем поле номера своего имени

                ncb.NetworkNameNumber = NetwrkNameNumber;
                ncb.Cmd = NB_WSendDatagram;

// Вызываем NETBIOS

                NetBios();
        }

// Создание канала с принимающей стороны

        void WListen(void) {
                ncb.Cmd = NB_WListen;
                NetBios();
        }

// Создание канала с передающей стороны

        void WCall(void) {
                ncb.Cmd = NB_WCall;
                NetBios();
        }

// Удаление канала

        void WHangUp(void) {
                ncb.Cmd = NB_WHangUp;
                NetBios();
        }

// Прием из канала с ожиданием

        void WReceive(void) {
                ncb.Cmd = NB_WReceive;
                NetBios();
        }

// Передача в канал с ожиданием

        void WSend(void) {
                ncb.Cmd = NB_WSend;
                NetBios();
        }
};