Книга: Системное программирование в среде Windows

Пример: сервер, использующий порты завершения ввода/вывода

Пример: сервер, использующий порты завершения ввода/вывода

Программа 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;
 /* Подавление предупреждающих сообщений компилятора. */
}
 

Оглавление книги


Генерация: 1.227. Запросов К БД/Cache: 3 / 1
поделиться
Вверх Вниз