Харт Джонсон М.
Шрифт:
В другом возможном варианте решения, отличающемся большей гибкостью, можно было бы закрывать дескриптор при каждом отсоединении клиента и создавать новый дескриптор для каждого нового подключения. Этот способ аналогичен тому, который использовался в случае сокетов в главе 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}; /*Для ответных сообщений.*/
/* Чтобы избежать помещения перекрывающейся операции в очередь порта завершения, должен быть установлен младший бит события. Несмотря на всю странность этого способа, он документирован. */