В этой главе мы рассмотрим службу передачи сообщений, которая имеется в операционной системе 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); }