Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Методичка ТВП ЛР A5.doc
Скачиваний:
3
Добавлен:
01.04.2025
Размер:
611.84 Кб
Скачать

Установка соединения с каналом со стороны сервера

После того как серверный процесс создал канал, он может перейти в режим соединения с клиентским процессом. Соединение со стороны сервера выполняется с помощью функции ConnectNamedPipe.

Прототип функции ConnectNamedPipe представлен ниже:

BOOL ConnectNamedPipe (

HANDLE hNamedPipe,

// идентификатор именованного канала

LPOVERLAPPED lpOverlapped);

// адрес структуры OVERLAPPED

Через первый параметр серверный процесс передает этой функции идентификатор канала, полученный от функции CreateNamedPipe.

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

В случае успеха функция ConnectNamedPipe возвращает значение TRUE, а при ошибке - FALSE. Код ошибки можно получить с помощью функции GetLastError.

В зависимости от различных условий функция ConnectNamedPipe может вести себя по разному.

Если параметр lpOverlapped указан как NULL, функция выполняется в синхронном режиме. В противном случае используется асинхронный режим.

Для канала, созданного в синхронном блокирующем режиме (с использованием константы PIPE_WAIT), функция ConnectNamedPipe переходит в состояние ожидания соединения с клиентским процессом. Именно этот режим мы будем использовать в наших примерах программ, исходные тексты которых вы найдете ниже.

Если канал создан в синхронном неблокирующем режиме, функция ConnectNamedPipe немедленно возвращает управление с кодом TRUE, если только клиент был отключен от данной реализации канала и возможно подключение этого клиента. В противном случае возвращается значение FALSE. Дальнейший анализ необходимо выполнять с помощью функции GetLastError. Эта функция может вернуть значение ERROR_PIPE_LISTENING (если к серверу еще не подключен ни один клиент), ERROR_PIPE_CONNECTED (если клиент уже подключен) или ERROR_NO_DATA (если предыдущий клиент отключился от сервера, но клиент еще не завершил соединение).

Ниже мы привели пример использования функции ConnectNamedPipe:

HANDLE hNamedPipe;

LPSTR lpszPipeName = "\\\\.\\pipe\\$MyPipe$";

hNamedPipe = CreateNamedPipe (lpszPipeName, PIPE_ACCESS_DUPLEX,

PIPE_TYPE_MESSAGE | PIPE_READMODE_MESSAGE | PIPE_WAIT,

PIPE_UNLIMITED_INSTANCES, 512, 512, 5000, NULL);

fConnected = ConnectNamedPipe (hNamedPipe, NULL);

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

Пример приложения, использующего каналы передачи данных Pipes

После изложения теоретического материала рассмотрим пример. Сначала рассмотрим серверную часть приложения.

Объявим переменные, которые будут использоваться в программе, и присвоим им необходимее значения:

BOOL fConnected; // Флаг успешного создания канала

HANDLE hNamedPipe; // Идентификатор канала Pipe

LPSTR lpszPipeName = "\\\\.\\pipe\\$MyPipe$"; // Имя создава-емого канала Pipe

char szBuf[512]; // Буфер для передачи данных через канал

DWORD cbRead; // Количество байт данных, принятых через канал

DWORD cbWritten; // Количество байт данных, переданных через канал

Создаем канал Pipe, имеющий имя lpszPipeName:

hNamedPipe = CreateNamedPipe (lpszPipeName, PIPE_ACCESS_DUPLEX,

PIPE_TYPE_MESSAGE | PIPE_READMODE_MESSAGE | PIPE_WAIT, PIPE_UNLIMITED_INSTANCES, 512, 512, 5000, NULL);

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

if(hNamedPipe == INVALID_HANDLE_VALUE)

{fprintf(stdout,"CreateNamedPipe: Error %ld\n", GetLastError ());

getch();

return 0;

}

Ожидаем соединения со стороны клиента:

fConnected = ConnectNamedPipe (hNamedPipe, NULL);

// При возникновении ошибки выводим ее код

if(!fConnected)

{ switch(GetLastError ())

{ case ERROR_NO_DATA:

fprintf(stdout,"ConnectNamedPipe: ERROR_NO_DATA");

getch();

CloseHandle (hNamedPipe);

return 0;

break;

case ERROR_PIPE_CONNECTED:

fprintf(stdout, "ConnectNamedPipe: ERROR_PIPE_CONNECTED");

getch();

CloseHandle (hNamedPipe);

return 0;

break;

case ERROR_PIPE_LISTENING:

fprintf(stdout,"ConnectNamedPipe: ERROR_PIPE_LISTENING");

getch();

CloseHandle (hNamedPipe);

return 0;

break;

case ERROR_CALL_NOT_IMPLEMENTED: fprintf(stdout,"ConnectNamedPipe: ERROR_CALL_NOT_IMPLEMENTED");

getch();

CloseHandle (hNamedPipe);

return 0;

break;

default:

fprintf(stdout,"ConnectNamedPipe: Error %ld\n", GetLastError ());

getch();

CloseHandle (hNamedPipe);

return 0;

break;

}

CloseHandle (hNamedPipe);

getch();

return 0;

}

Цикл получения команд через канал:

while(TRUE)

{ // Получаем очередную команду через канал Pipe if(ReadFile (hNamedPipe, szBuf, 512, &cbRead, NULL))

{ // Посылаем эту команду обратно клиентскому приложению

if(!WriteFile (hNamedPipe, szBuf, strlen(szBuf) + 1, &cbWritten, NULL)) break;

// Выводим принятую команду на консоль

printf("Received: <%s>\n", szBuf);

// Если пришла команда "exit", завершаем работу приложения

if(!strcmp(szBuf, "exit")) break;

}

else

{

fprintf(stdout,"ReadFile: Error %ld\n",

GetLastError ());

getch();

break;

}

}

Закрываем идентификатор канала:

CloseHandle (hNamedPipe);

А теперь рассмотрим клиентскую часть приложения.

Объявим переменные, которые будут использоваться в программе, и присвоим им необходимее значения:

HANDLE hNamedPipe; // Идентификатор канала Pipe

DWORD cbWritten; // Количество байт, переданных через канал

DWORD cbRead; // Количество байт, принятых через канал

char szBuf[256]; // Буфер для передачи данных

char szPipeName[256]= "\\\\%s\\pipe\\$MyPipe$"// Буфер для имени канала

Создаем канал с процессом PIPES:

hNamedPipe = CreateFile (szPipeName, GENERIC_READ | GENERIC_WRITE, 0, NULL,

OPEN_EXISTING, 0, NULL);

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

if(hNamedPipe == INVALID_HANDLE_VALUE)

{ fprintf(stdout,"CreateFile: Error %ld\n", GetLastError ());

getch();

return 0;

}

Цикл обмена данными с серверным процессом:

while(TRUE)

{ // Выводим приглашение для ввода команды

printf("cmd>");

// Вводим текстовую строку

gets(szBuf);

// Передаем введенную строку серверному процессу в качестве команды

if(!WriteFile (hNamedPipe, szBuf, strlen(szBuf) + 1, &cbWritten, NULL)) break;

// Получаем эту же команду обратно от сервера

if(ReadFile (hNamedPipe, szBuf, 512, &cbRead, NULL)) printf("Received back: <%s>\n", szBuf);

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

else

{ fprintf(stdout,"ReadFile: Error %ld\n", GetLastError ());

getch();

break;

}

// В ответ на команду "exit" завершаем цикл обмена данными с серверным процессом

if(!strcmp(szBuf, "exit")) break;

}

Закрываем идентификатор канала:

CloseHandle (hNamedPipe);

7.4. Каналы передачи данных Mailslot

Рассмотрим последний способ передачи данных между приложениями: каналы MailSlot.

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

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

С помощью каналов Pipe вы не сможете передавать данные в широковещательном режиме, так как только два процесса могут создать канал типа Pipe.