7. Передача и прием сообщений

В этой главе мы рассмотрим службу передачи сообщений, которая имеется в операционной системе Novell NetWare. Эта служба позволяет организовать передачу коротких сообщений между рабочими станциями с использованием ресурсов файл-сервера. Например, утилита SEND операционной системы Novell NetWare передает сообщения именно с помощью описанных в этой главе средств. Мы расскажем вам не о всех возможностях системы передачи сообщений, а только о самой интересной, на наш взгляд, - возможности передачи сообщений от одной рабочей станции на другие и на файл-сервер. Об организации передачи сообщений через каналы (Pipes) вы можете узнать из документации, поставляющейся вместе с библиотекой функций NetWare C Interface.

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

Для передачи сообщения на другие рабочие станции программа должна использовать функцию SendBroadcastMessage(). Можно передать сообщение и на консоль файл-сервера, для этого используется функция BroadcastToConsole().

7.1. Режимы приема сообщений

Что происходит, когда рабочая станция принимает сообщение? Это зависит от того, кто послал сообщение (другой пользователь или файл-сервер), а также от режима приема сообщений, установленном на рабочей станции.

Станция может принимать сообщения в четырех режимах:

0 Этот режим используется по умолчанию и устанавливается сразу после загрузки сетевой оболочки. Когда приходит сообщение, сетевая оболочка автоматически отображает сообщение в нижней строке экрана, но только в том случае, если установлен текстовый режим работы. В графических режимах работы сообщение не отображается
1 В этом режиме файл-сервер запоминает в буфере приходящие от других пользователей сообщения, но сетевая оболочка отображает только сообщения, которые пришли от файл-сервера
2 В этом режиме файл-сервер игнорирует сообщения от других пользователей, запоминая в буфере только сообщения от файл-сервера. Автоматический вывод сообщения на экран не выполняется
3 Файл-сервер запоминает в буфере как сообщения, пришедшие от пользователей, так и сообщения файл-сервера. Автоматический вывод сообщения на экран не выполняется

Для установки режима используется функция SetBroadcastMode(), текущий режим можно определить с помощью функции GetBroadcastMode().

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

7.2. Определение режима приема сообщений

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

BYTE GetBroadcastMode(void);


Функция возвращает значение в диапазона от 0 до 3, соответствующее текущему режиму приема сообщений.

Вместо функции GetBroadcastMode() для определения текущего режима приема сообщений вы можете воспользоваться функцией DEh прерывания INT 21h:

На входе: AH = DEh;
DL = В регистр DL необходимо загрузить значение 04h.
На выходе: AL = Номер текущего режима приема сообщений (0, 1, 2, 3).

7.3. Установка режима приема сообщений

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

void SetBroadcastMode(BYTE BroadcastMode);


Параметр BroadcastMode определяет новый режим приема сообщений.

Вместо функции SetBroadcastMode() для определения текущего режима приема сообщений вы можете воспользоваться функцией DEh прерывания INT 21h:

На входе: AH = DEh;
DL = В регистр DL необходимо загрузить новое значение режима приема сообщений (0, 1, 2, 3).
На выходе: Регистры не используются.

7.4. Передача сообщений пользователям

Для передачи сообщения другим пользователям предназначена функция SendBroadcastMessage():

int SendBroadcastMessage(char *Message, WORD *ConnectionList,
       BYTE *ResultList, WORD ConnectionCount);


Параметр Message задает адрес текстовой строки, содержащей сообщение. Размер этой строки не должен превышать 56 байт (включая двоичный ноль, закрывающий строку).

Параметр ConnectionList - указатель на массив слов, содержащий номера каналов, используемых файл-сервером для связи с рабочими станциями. Размер этого массива определяется параметром ConnectionCount.

Параметр ResultList - массив байт, в котором для каждой станции отражается результат посылки сообщения:

0x00 Сообщение передано успешно
0xFC Сообщение не передано, так как буфер сообщения для данной станции уже содержит сообщение, которое еще не было принято станцией
0xFD Соответствующий номер канала задан неправильно
0xFF Станция заблокировала прием сообщения, установив соответствующий режим приема сообщений. Этот код ошибки может появиться и в том случае, если заданный номер канала не используется

Функция возвращает 0 при успешном завершении или код ошибки:

Код ошибки Значение
0xFE Ошибка ввода/вывода или нехватка памяти на сервере

Вместо функции SendBroadcastMessage() можно использовать функцию E1h прерывания INT 21h:

На входе: AH = E1h;
DS:SI = Адрес буфера запроса;
ES:DI = Адрес буфера ответа.
На выходе: AL = Код ошибки или 0, если операция завершилась без ошибок.

Буфер запроса:

struct REQUEST {
        WORD    PacketLength;        // размер пакета запроса
        BYTE    Function;            // должно быть равно 0
        BYTE    ConnectionCount;     // количество станций
        BYTE    ConnectionList[ConnectionCount];// список станций
        BYTE    MessageLength;       // длина сообщения
        BYTE    Message[MessageLength]; // сообщение
};


Буфер ответа:

struct REPLAY {
        WORD    PacketLength;        // размер пакета
        BYTE    ConnectionCount;     // количество станций
        BYTE    ResultList[ConnectionCount];// результат
};


7.4.1. Программа MSGSEND

Программа MSGSEND (листинг 28) передает сообщение, заданное в качестве параметра, всем пользователям, подключенным к файл-серверу. Перед посылкой сообщения она с помощью функции BroadcastToConsole() выводит текстовую строку на консоль файл-сервера и записывает эту же строку в системный журнал net$log.msg, вызывая функцию LogNetworkMessage().

Для получения списка пользователей программа использует функцию GetConnectionInformation(). Эта функция возвращает информацию о пользователе, подключенном к файл-серверу, по номеру канала. Каналы нумеруются начиная с первого. Максимальное количество каналов определяется версией операционной системы, его можно определить при помощи функции GetServerInformation().

Сканируя все каналы, программа подготавливает массив номеров каналов ConnectionList[] для функции SendBroadcastMessage(), которая и выполняет рассылку сообщений.

// ===================================================
// Листинг 28. Посылка сообщения станциям
// Файл msgsend\msgsend.cpp
//
// (C) A. Frolov, 1993
// ===================================================

#include <stdlib.h>
#include <stdio.h>

#define WORD unsigned int
#define BYTE unsigned char

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

extern "C" int GetNetWareShellVersion(char *,char *, char *);
extern "C" int BroadcastToConsole(char *);
extern "C" int SendBroadcastMessage(char*, WORD*, BYTE*, WORD);
extern "C" int LogNetworkMessage(char*);
extern "C" void GetServerInformation(int, FILE_SERV_INFO*);
extern "C" int GetConnectionInformation(WORD, char *, WORD *,
                                                long *, BYTE *);

void main(int argc, char *argv[]) {

        char MajorVersion=0;
        char MinorVersion=0;
        char Revision=0;

        long ObjectID;
        char ObjectName[48];
        WORD ObjectType;
        BYTE LoginTime;
        FILE_SERV_INFO ServerInfo;
        int MaxUsers;

        WORD ConnectionList[250];
        BYTE ResultList[250];
        WORD ConnectionCount;

        printf("\n*MSGSEND* (C) Frolov A., 1993\n");

        if(argc < 2) {
                printf("Введите сообщение в качестве параметра\n");
                return;
        }

// Проверяем присутствие сетевой оболочки

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

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

// Выводим сообщение на консоль файл-сервера и
// записываем его в журнал

        BroadcastToConsole("*MSGSEND* (C) Frolov A., 1993");
        LogNetworkMessage("*MSGSEND* (C) Frolov A., 1993");

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

        GetServerInformation(sizeof(ServerInfo), &ServerInfo);

// Запоминаем максимальное количество пользователей

        MaxUsers = ServerInfo.maxConnectionsSupported;
        printf("Сервер %s, версия на %d пользователей\n",
                ServerInfo.serverName,  MaxUsers);

// Цикл посылки сообщений. Подсчитываем количество используемых
// каналов и для каждого канала заполняем массив ConnectionList[]

        printf("\nСообщение посылается пользователям:\n");

        ConnectionCount = 0;

        for(int i=1, j=0; i <= MaxUsers; i++) {

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

                GetConnectionInformation(i, ObjectName, &ObjectType,
                                &ObjectID, &LoginTime);

// Если есть имя объекта, выводим его на экран

                if(ObjectName[0] != '\0') {
                        printf("%s\n", ObjectName);

// Записываем номер канала в массив

                        ConnectionList[j++] = i;
                        ConnectionCount += 1;
                }
        }

// Посылаем сообщение обнаруженным пользователям

        SendBroadcastMessage(argv[1],
                ConnectionList, ResultList, ConnectionCount);
}


7.5. Прием сообщений

Для приема сообщений предназначена функция GetBroadcastMessage():

int GetBroadcastMessage(char *MessageBuffer);


Параметр определяет адрес буфера, в который будет записано принятое сообщение. Размер буфера должен составлять не менее 56 байт.

Функция возвращает 0 при успешном завершении или код ошибки:

Код ошибки Значение
0xFC Переполнение очереди сообщений
0xFE Ошибка ввода/вывода или нехватка памяти на сервере

Если в буфере нет сообщений, в первый байт буфера будет записано нулевое значение.

Вместо функции GetBroadcastMessage() можно использовать функцию E1h прерывания INT 21h:

На входе: AH = E1h;
DS:SI = Адрес буфера запроса;
ES:DI = Адрес буфера ответа.
На выходе: AL = Код ошибки или 0, если операция завершилась без ошибок.

Буфер запроса:

struct REQUEST {
        WORD    PacketLength;        // размер пакета запроса
        BYTE    Function;            // должно быть равно 1
};


Буфер ответа:

struct REPLAY {
        WORD    PacketLength;        // размер пакета
        BYTE    MessageLength;       // длина сообщения
        BYTE    Message[MessageLength]; // сообщение
};


7.5.1. Программа MSGRCV

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

Перед завершением работы восстанавливается старый режим приема сообщений.

// ===================================================
// Листинг 29. Прием сообщений
// Файл msgrcv\msgrcv.cpp
//
// (C) A. Frolov, 1993
// ===================================================

#include <stdlib.h>
#include <stdio.h>
#include <conio.h>

#define WORD unsigned int
#define BYTE unsigned char

extern "C" int GetNetWareShellVersion(char *,char *, char *);
extern "C" int GetBroadcastMessage(char*);
extern "C" BYTE GetBroadcastMode(void);
extern "C" void SetBroadcastMode(BYTE);

void main(void) {

        char MajorVersion=0;
        char MinorVersion=0;
        char Revision=0;

        BYTE OldBroadcastMode;
        char MessageBuffer[56];
        int ccode;

        printf("\n*MSGRCV* (C) Frolov A., 1993\n");

// Проверяем присутствие сетевой оболочки

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

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

// Сохраняем старый режим приема сообщений

        OldBroadcastMode = GetBroadcastMode();

// Устанавливаем режим, при котором сообщения от файл-сервера и поль-
// зователей записываются в буфер, но автоматически не отображаются

        SetBroadcastMode(3);

// Ожидаем прихода сообщения

        for(;;) {

// Извлекаем сообщение из буфера

                ccode = GetBroadcastMessage(MessageBuffer);
                if(ccode) break;

// Если сообщение есть в буфере, выводим его

                if(MessageBuffer[0] != '\0') {
                        printf(">>> %s\n", MessageBuffer);
                }

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

                if(kbhit()) break;
        }
// Восстанавливаем старый режим приема сообщений

        SetBroadcastMode(OldBroadcastMode);
}