Пример: сервер, использующий порты завершения ввода/вывода
Пример: сервер, использующий порты завершения ввода/вывода
Программа 14.4 представляет видоизмененный вариант программы serverNP (программа 11.3), в котором используются порты завершения ввода/вывода. Этот сервер создает небольшой пул серверных потоков и больший пул дескрипторов перекрывающихся каналов, а также ключей завершения, по одному для каждого дескриптора. Перекрывающиеся дескрипторы присоединяются к порту завершения, а затем вызывается функция ConnectNamedPipe. Серверные потоки ожидают сигналов завершения, связанных как с подключениями клиентов, так и с операциями чтения. Когда регистрируется операция чтения, обрабатывается соответствующий клиентский запрос, и результаты возвращаются без использования порта завершения. Вместо этого серверный поток ожидает наступления события после выполнения операции записи, причем младший бит дескриптора события в структуре OVERLAPPED устанавливается в 1.
В другом возможном варианте решения, отличающемся большей гибкостью, можно было бы закрывать дескриптор при каждом отсоединении клиента и создавать новый дескриптор для каждого нового подключения. Этот способ аналогичен тому, который использовался в случае сокетов в главе 12. Вместе с тем, имеется одна трудность, обусловленная невозможностью удаления дескрипторов из порта завершения, в результате чего использование короткоживущих дескрипторов подобного рода будет приводить к утечке ресурсов.
Поскольку с большей частью кода вы уже знакомы по предыдущим примерам, она здесь не приводится.
Программа 14.4. serverCP: сервер, использующий порт завершения
/* Глава 14. ServerCP. Многопоточный сервер.
Версия на основе именованного канала, пример ПОРТА ЗАВЕРШЕНИЯ.
Использование: Server [ИмяПользователя ИмяГруппы]. */
#include "EvryThng.h"
#include "ClntSrvr.h"
/* Здесь определяются сообщения запроса и ответа. */
typedef struct { /*Структуры, на которые указывают ключи портов завершения*/
HANDLE hNp; /* и которые представляют еще не выполненные операции */
REQUEST Req; /* ReadFile и ConnectNamedPipe. */
DWORD Type; /* 0 – ConnectNamedPipe; 1 – ReadFile. */
OVERLAPPED Ov;
} CP_KEY;
static CP_KEY Key[MAX_CLIENTS_CP]; /* Доступно всем потокам. */
/* … */
_tmain(int argc, LPTSTR argv[]) {
HANDLE hCp, hMonitor, hSrvrThread[MAXCLIENTS];
DWORD iNp, iTh, MonitorId, ThreadId;
THREAD_ARG ThArgs[MAX_SERVER_TH];
/*…*/
hCp = CreateIoCompletionPort(INVALID_HANDLE_VALUE, NULL, 0, MAX_SERVER_TH);
/* Создать перекрывающийся именованный канал для каждого потенциального */
/* клиента, добавить порт завершения и ожидать соединения. */
/* Предполагается, что максимальное количество клиентов намного */
/* превышает количество серверных потоков. */
for (iNp = 0; iNp < MAX_CLIENTS_CP; iNp++) {
memset(&Key[iNp], 0, sizeof(CP_KEY));
Key[iNp].hNp = CreateNamedPipe(SERVER_PIPE, PIPE_ACCESS_DUPLEX | FILE_FLAG_OVERLAPPED, PIPE_READMODE_MESSAGE | PIPE_TYPE_MESSAGE | PIPE_WAIT, MAX_CLIENTS_CP, 0, 0, INFINITE, pNPSA);
CreateIoCompletionPort(Key[iNp].hNp, hCp, iNp, MAX_SERVER_TH + 2);
Key[iNp].Ov.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
ConnectNamedPipe(Key[iNp].hNp, &Key[iNp].Ov);
}
/* Создать рабочие серверные потоки и имя временного файла для каждой из них.*/
for (iTh = 0; iTh < MAX_SERVER_TH; iTh++) {
ThArgs[iTh].hCompPort = hCp;
ThArgs[iTh].ThreadNo = iTh;
GetTempFileName(_T("."), _T("CLP"), 0, ThArgs[iTh].TmpFileName);
hSrvrThread[iTh] = (HANDLE)_beginthreadex (NULL, 0, Server, &ThArgs[iTh], 0, &ThreadId);
}
/* Дождаться завершения всех потоков и "убрать мусор". */
/* … */
return 0;
}
static DWORD WINAPI Server(LPTHREAD_ARG pThArg)
/* Функция потока сервера.
Имеется по одному потоку для каждого потенциального клиента. */
{
HANDLE hCp, hTmpFile = INVALID_HANDLE_VALUE;
HANDLE hWrEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
DWORD nXfer, KeyIndex, ServerNumber;
/* … */
BOOL Success, Disconnect, Exit = FALSE;
LPOVERLAPPED pOv;
OVERLAPPED ovResp = {0, 0, 0, 0, hWrEvent}; /*Для ответных сообщений.*/
/* Чтобы избежать помещения перекрывающейся операции в очередь порта завершения, должен быть установлен младший бит события. Несмотря на всю странность этого способа, он документирован. */
ovResp.hEvent = (HANDLE)((DWORD)hWrEvent | 0x1);
GetStartupInfo(&StartInfoCh);
hCp = pThArg->hCompPort;
ServerNumber = pThArg->ThreadNo;
while(!ShutDown && !Exit) __try {
Success = FALSE; /* Устанавливается только в случае успешного завершения всех операций. */
Disconnect = FALSE;
GetQueuedCompletionStatus(hCp, &nXfer, &KeyIndex, &pOv, INFINITE);
if (Key [KeyIndex].Type == 0) { /* Соединение установлено. */
/* Открыть временный файл с результатами для этого соединения. */
hTmpFile = CreateFile(pThArg->TmpFileName, /* … */);
Key[KeyIndex].Type = 1;
Disconnect = !ReadFile(Key[KeyIndex].hNp, &Key[KeyIndex].Req, RQ_SIZE, &nXfer, &Key[KeyIndex].Ov) && GetLastError () == ERROR_HANDLE_EOF; /* Первая операция чтения. */
if (Disconnect) continue;
Success = TRUE;
} else {
/* Чтение завершилось. Обработать запрос. */
ShutDown = ShutDown || (_tcscmp (Key[KeyIndex].Req.Record, ShutRqst) == 0);
if (ShutDown) continue;
/* Создать процесс для выполнения команды. */
/* … */
/* Отвечать по одной строке за один раз. На данном этапе удобно использовать функции библиотеки С для работы со строками. */
fp = _tfopen(pThArg->TmpFileName, _T("r"));
Response.Status = 0;
/* Поскольку младший бит события установлен, ответные сообщения в очередь порта завершения не помещаются. */
while(_fgetts(Response.Record, MAX_RQRS_LEN, fp) != NULL) {
WriteFile(Key [KeyIndex].hNp, &Response, RS_SIZE, &nXfer, &ovResp);
WaitForSingleObject(hWrEvent, INFINITE);
}
fclose(fp);
/* Уничтожить содержимое временного файла. */
SetFilePointer(hTmpFile, 0, NULL, FILE_BEGIN);
SetEndOfFile(hTmpFile);
/* Отправить признак конца ответа. */
Response.Status = 1;
strcpy(Response.Record, "");
WriteFile(Key[KeyIndex].hNp, &Response, RS_SIZE, &nXfer, &ovResp);
WaitForSingleObject(hWrEvent, INFINITE);
/* Конец основного командного цикла. Получить следующую команду.*/
Disconnect = !ReadFile(Key[KeyIndex].hNp, &Key[KeyIndex].Req, RQ_SIZE, &nXfer, &Key[KeyIndex].Ov) && GetLastError() == ERROR_HANDLE_EOF; /* Следующее чтение */
if (Disconnect) continue;
Success = TRUE;
}
} __finally {
if (Disconnect) {
/* Создать еще одно соединение по этому каналу. */
Key[KeyIndex].Type = 0;
DisconnectNamedPipe(Key[KeyIndex].hNp);
ConnectNamedPipe(Key[KeyIndex].hNp, &Key[KeyIndex].Ov);
}
if (!Success) {
ReportError(_T("Ошибка сервера"), 0, TRUE);
Exit = TRUE;
}
}
FlushFileBuffers(Key[KeyIndex].hNp);
DisconnectNamedPipe(Key[KeyIndex].hNp);
CloseHandle(hTmpFile);
/* … */
_endthreadex(0);
return 0;
/* Подавление предупреждающих сообщений компилятора. */
}
Более 800 000 книг и аудиокниг! 📚
Получи 2 месяца Литрес Подписки в подарок и наслаждайся неограниченным чтением
ПОЛУЧИТЬ ПОДАРОКЧитайте также
Порты завершения ввода/вывода
Порты завершения ввода/вывода В главе 14 описываются порты завершения ввода/вывода, которые предоставляют другой механизм, позволяющий избежать состязательности между потоками путем ограничения их количества. Порты завершения ввода/вывода дают возможность небольшому
ГЛАВА 14 Асинхронный ввод/вывод и порты завершения
ГЛАВА 14 Асинхронный ввод/вывод и порты завершения Операциям ввода и вывода присуща более медленная скорость выполнения по сравнению с другими видами обработки. Причиной такого замедления являются следующие факторы:• Задержки, обусловленные затратами времени на поиск
Пример: преобразование файлов с использованием перекрывающегося ввода/вывода и множественной буферизации
Пример: преобразование файлов с использованием перекрывающегося ввода/вывода и множественной буферизации Программа 2.4 (atou) осуществляла преобразование ASCII-файла к кодировке UNICODE путем последовательной обработки файла, а в главе 5 было показано, как выполнить такую же
Пример: преобразование файла с использованием расширенного ввода/вывода
Пример: преобразование файла с использованием расширенного ввода/вывода Программа 14.3 (atouEX) представляет собой переработанную версию программы 14.1. Эти программы иллюстрируют различие между двумя методами асинхронного ввода/вывода. Программа atouEx аналогична программе
Порты завершения ввода/вывода
Порты завершения ввода/вывода Порты завершения ввода/вывода, поддерживаемые лишь на NT-платформах, объединяют в себе возможности перекрывающегося ввода/вывода и независимых потоков и используются чаще всего в серверных программах. Чтобы выяснить, какими требованиями
Управление портами завершения ввода/вывода
Управление портами завершения ввода/вывода Для создания порта и присоединения к нему дескрипторов используется одна и та же функция — CreateCompletionPort. Необходимость выполнения этой функцией двух разных задач соответственно усложняет использование ее параметров. HANDLE
Ожидание порта завершения ввода/вывода
Ожидание порта завершения ввода/вывода Для выполнения ввода/вывода с участием дескрипторов, связанных с портом, используются функции ReadFile и WriteFile со структурами OVERLAPPED (дескрипторы событий не требуются). Далее операция ввода/вывода помещается в очередь порта
Отправка уведомления порту завершения ввода/вывода
Отправка уведомления порту завершения ввода/вывода Поток может отправить в порт событие завершения вместе с ключом, чтобы завершить остающийся невыполненным вызов функции GetQueueCompletionStatus. Вся необходимая для этого информация предоставляется функцией PostQueueCompletionStatus. BOOL
Операции ввода-вывода в AS/400
Операции ввода-вывода в AS/400 Теперь от аппаратной архитектуры ввода-вывода AS/400 перейдем к совместной работе OS/400, SLIC и аппаратуры при выполнении операции ввода-вывода для прикладной программы. Сначала рассмотрим объекты, поддерживающие ввод-вывод, затем — многоуровневую
Подсистема ввода/вывода
Подсистема ввода/вывода Подсистема ввода/вывода выполняет запросы файловой подсистемы и подсистемы управления процессами для доступа к периферийным устройствам (дискам, магнитным лентам, терминалам и т.д.). Она обеспечивает необходимую буферизацию данных и
Операции ввода/вывода
Операции ввода/вывода На рис. 4.14 представлена схема выполнения операций ввода/вывода с использованием буферного кэша. Важной особенностью этой подсистемы является то, что она обеспечивает независимое выполнение операций чтения или записи данных процессом как
8.15. Эхо-сервер TCP и UDP, использующий функцию select
8.15. Эхо-сервер TCP и UDP, использующий функцию select Теперь мы объединим наш параллельный эхо-сервер TCP из главы 5 и наш последовательный эхо-сервер UDP из данной главы в один сервер, использующий функцию select для мультиплексирования сокетов TCP и UDP. В листинге 8.14 представлена
СИСТЕМНО-ЗАВИСИМЫЕ СРЕДСТВА: ПОРТЫ ВВОДА-ВЫВОДА МИКРОПРОЦЕССОРОВ INTEL 8086/8088
СИСТЕМНО-ЗАВИСИМЫЕ СРЕДСТВА: ПОРТЫ ВВОДА-ВЫВОДА МИКРОПРОЦЕССОРОВ INTEL 8086/8088 Рассмотрим различные устройства ввода-вывода, поскольку теперь мы хотим обсудить вопрос о том, как приспособить реализацию компилятора с языка Си к требованиям конкретной вычислительной
Пример 3-1. Вложенные блоки и перенаправление ввода-вывода
Пример 3-1. Вложенные блоки и перенаправление ввода-вывода #!/bin/bash# Чтение строк из файла /etc/fstab.File=/etc/fstab{read line1read line2} < $Fileecho "Первая строка в $File :"echo "$line1"echoecho "Вторая строка в $File :"echo "$line2"exit