7.2. Определение режима приема сообщений
7.3. Установка режима приема сообщений
7.4. Передача сообщений пользователям
7.5. Прием сообщений
В этой главе мы рассмотрим службу передачи сообщений, которая имеется в операционной системе Novell NetWare. Эта служба позволяет организовать передачу коротких сообщений между рабочими станциями с использованием ресурсов файл-сервера. Например, утилита SEND операционной системы Novell NetWare передает сообщения именно с помощью описанных в этой главе средств. Мы расскажем вам не о всех возможностях системы передачи сообщений, а только о самой интересной, на наш взгляд, - возможности передачи сообщений от одной рабочей станции на другие и на файл-сервер. Об организации передачи сообщений через каналы (Pipes) вы можете узнать из документации, поставляющейся вместе с библиотекой функций NetWare C Interface.
Работа системы передачи сообщений основана на том, что файл-сервер для каждой подключенной к нему рабочей станции создает буфер размером 55 байт. Этот буфер используется для временного хранения сообщения, предназначенного для рабочей станции. Помимо сообщений от рабочих станций файл-сервер может передавать свои собственные сообщения, например сообщение о завершении своей работы.
Для передачи сообщения на другие рабочие станции программа должна использовать функцию SendBroadcastMessage(). Можно передать сообщение и на консоль файл-сервера, для этого используется функция BroadcastToConsole().
Что происходит, когда рабочая станция принимает сообщение? Это зависит от того, кто послал сообщение (другой пользователь или файл-сервер), а также от режима приема сообщений, установленном на рабочей станции.
Станция может принимать сообщения в четырех режимах:
| 0 | Этот режим используется по умолчанию и устанавливается сразу после загрузки сетевой оболочки. Когда приходит сообщение, сетевая оболочка автоматически отображает сообщение в нижней строке экрана, но только в том случае, если установлен текстовый режим работы. В графических режимах работы сообщение не отображается |
| 1 | В этом режиме файл-сервер запоминает в буфере приходящие от других пользователей сообщения, но сетевая оболочка отображает только сообщения, которые пришли от файл-сервера |
| 2 | В этом режиме файл-сервер игнорирует сообщения от других пользователей, запоминая в буфере только сообщения от файл-сервера. Автоматический вывод сообщения на экран не выполняется |
| 3 | Файл-сервер запоминает в буфере как сообщения, пришедшие от пользователей, так и сообщения файл-сервера. Автоматический вывод сообщения на экран не выполняется |
Для установки режима используется функция SetBroadcastMode(), текущий режим можно определить с помощью функции GetBroadcastMode().
Если сообщение не отображается автоматически, программа, запущенная на рабочей станции, может извлечь его из буфера при помощи функции GetBroadcastMessage(). Например, при работе в графическом режиме ваша программа должна уметь получать сообщения и отображать их, так как сетевая оболочка отображает сообщения только в текстовом режиме работы видеоадаптера.
Первое, что должна сделать программа, обрабатывающая сообщения, это определить текущий режим приема сообщений. Для этого она должна вызвать функцию GetBroadcastMode():
BYTE GetBroadcastMode(void);
Функция возвращает значение в диапазона от 0 до 3, соответствующее текущему режиму приема сообщений.
Вместо функции GetBroadcastMode() для определения текущего режима приема сообщений вы можете воспользоваться функцией DEh прерывания INT 21h:
| На входе: | AH | = | DEh; |
| DL | = | В регистр DL необходимо загрузить значение 04h. | |
| На выходе: | AL | = | Номер текущего режима приема сообщений (0, 1, 2, 3). |
Перед завершением работы ваша программа должна восстановить режим обработки сообщений (если задачей программы не является изменение этого режима). Для восстановления режима воспользуйтесь функцией SetBroadcastMode():
void SetBroadcastMode(BYTE BroadcastMode);
Параметр BroadcastMode определяет новый режим приема сообщений.
Вместо функции SetBroadcastMode() для определения текущего режима приема сообщений вы можете воспользоваться функцией DEh прерывания INT 21h:
| На входе: | AH | = | DEh; |
| DL | = | В регистр DL необходимо загрузить новое значение режима приема сообщений (0, 1, 2, 3). | |
| На выходе: | Регистры не используются. |
Для передачи сообщения другим пользователям предназначена функция 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];// результат
};
Программа 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);
}
Для приема сообщений предназначена функция 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]; // сообщение
};
Программа 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);
}