Создание, подключение и именование каналов и почтовых ящиков

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

Аналогичная информация для почтовых ящиков приведена в табл. 11.2. Вспомните, что почтовый клиент (или сервер) не обязательно должен выполняться тем же процессом или даже на той же системе, что и клиент (или сервер) приложения.

 

Таблица 11.1. Именованные каналы: создание, подключение и именование

  Клиент приложения Сервер приложения
Дескриптор именованного канала или соединение CreateFile CallNamedPipe TransactNamedPipe CreateNamedPipe
Имя канала \\.\имя канала (канал является локальным) \\имя системы\имя канала (канал является локальным или удаленным) \\.\имя канала (канал создается локальным)

Таблица 11.2. Почтовые ящики: создание, подключение и именование

  Почтовый клиент Почтовый сервер
Дескриптор почтового ящика CreateFile CreateMailslot
Имя почтового ящика \\.\имя почтового ящика (почтовый ящик является локальным) \\имя системы\имя почтового ящика (почтовый ящик располагается на указанной удаленной системе) \\*\имя почтового ящика (все почтовые ящики, имеющие одно и то же указанное имя) \\.\имя почтового ящика (почтовый ящик создается локальным)

Пример: сервер, обнаруживаемый клиентами

Программа 11.4 представляет функцию потока, которую сервер командной строки (программа 11.3),выступающий в роли почтового клиента, использует для широковещательной рассылки имени своего канала ожидающим клиентам. Возможно существование нескольких серверов с различными характеристиками и именами каналов, и клиенты получают их имена, используя почтовый ящик с известным именем. Эта функция запускается как поток программой 11.3.

Примечание

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

Программа 11.4. SrvrBcst: функция потока почтового клиента

static DWORD WINAPI ServerBroadcast(LPLONG pNull) {

MS_MESSAGE MsNotify;

DWORD nXfer;

HANDLE hMsFile;

/*Открыть почтовый ящик для записывающей программы почтового "клиента"*/

while (!ShutDown) { /* Цикл выполняется до тех пор, пока имеются серверные потоки. */

/* Ждать, пока другой клиент не откроет почтовый ящик. */

Sleep(CS_TIMEOUT);

hMsFile = CreateFile(MS_CLTNAME, GENERIC_WRITE, FILE_SHARE_READ, NULL, OPEN EXISTING, FILE ATTRIBUTE NORMAL, NULL);

if (hMsFile == INVALID_HANDLE_VALUE) continue;

/* Отправить сообщение в почтовый ящик. */

MsNotify.msStatus = 0;

MsNotify.msUtilization = 0;

_tcscpy(MsNotify.msName, SERVER_PIPE);

if (WriteFile(hMsFile, &MsNotify, MSM_SIZE, &nXfer, NULL)) ReportError(_T("Ошибка записи почтового сервера."), 13, TRUE);

CloseHandle(hMsFile);

}

_tprintf(_T("Закрытие контролирующего потока.\n"));

_endthreadex(0);

return 0;

}

В программе 11.5 представлена функция, которая вызывается клиентом (см. программу 11.2) для обнаружения сервера.

Программа 11.5. LocSrvr: почтовый сервер

/* Глава 11. LocSrver.c */

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

 

#include "EvryThng.h"

#include "ClntSrvr.h" /* Определяет имя почтового ящика. */

 

BOOL LocateServer(LPTSTR pPipeName) {

HANDLE MsFile;

MS_MESSAGE ServerMsg;

BOOL Found = FALSE;

DWORD cbRead;

MsFile = CreateMailslot(MS_SRVNAME, 0, CS_TIMEOUT, NULL);

while (!Found) {

_tprintf(_T("Поиск сервера.\n"));

Found = ReadFile(MsFile, &ServerMsg, MSM_SIZE, &cbRead, NULL);

}

_tprintf(_T("Сервер найден.\n"));

CloseHandle(MsFile);

/* Имя канала сервера. */

_tcscpy(pPipeName, ServerMsg.msName);

return TRUE;

}