- •Процессы и задачи в Microsoft Windows
- •Запуск задач
- •Управление запущенными задачами
- •Изменение приоритета задачи
- •Определение приоритета задачи
- •Приостановка и возобновление выполнения задачи
- •Временная приостановка работы задачи
- •Завершение задачи
- •Освобождение идентификатора задачи
- •Критические секции
- •Инициализация критической секции
- •Удаление критической секции
- •Вход в критическую секцию и выход из нее
- •4. Контрольные вопросы
- •5. Содержание работы
- •Создание объекта - событие
- •Открытие объекта - событие
- •Управление состоянием объекта - событие
- •Объекты – событие с автосбросом
- •Создание объекта Mutex
- •Освобождение идентификатора объекта Mutex
- •Открытие объекта Mutex
- •Как завладеть объектом Mutex
- •Освобождение объекта Mutex
- •Как работает семафор
- •Функции для работы с семафорами
- •Создание семафора
- •Уничтожение семафора •
- •Увеличение значения счетчика семафора
- •Уменьшение значения счетчика семафора
- •Определение текущего значения счетчика семафора
- •Создание и открытие ожидаемого таймера
- •Функции для работы с ожидаемым таймером
- •Контрольные вопросы
- •Содержание работы
- •Содержание отчета
- •Теоретический материал
- •7.1. Универсальные функции для работы с файлами в операционных системах Microsoft Windows
- •Функция CreateFile
- •Функция CloseHandle
- •Функции ReadFile и WriteFile
- •Механизм отображения файлов на память
- •Создание отображения файла
- •Выполнение отображения файла в память
- •Открытие отображения
- •Отмена отображения файла
- •Принудительная запись измененных данных
- •Обмен через файлы, отображаемые на память
- •Пример приложения, использующего файлы, отображаемые на память
- •Именованные и анонимные каналы
- •Имена каналов
- •Реализации каналов
- •Создание канала
- •Установка соединения с каналом со стороны сервера
- •Пример приложения, использующего каналы передачи данных Pipes
- •Создание канала Mailslot
- •Открытие канала Mailslot
- •Запись сообщений в канал Mailslot
- •Чтение сообщений из канала Mailslot
- •Определение состояния канала Mailslot
- •Изменение состояния канала Mailslot
- •Пример приложения, использующего каналы передачи данных MailSlot
- •Пример приложения, использующего передачу сообщений между процессами
Установка соединения с каналом со стороны сервера
После того как серверный процесс создал канал, он может перейти в режим соединения с клиентским процессом. Соединение со стороны сервера выполняется с помощью функции 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.
