Категории
Самые читаемые
💎Читать книги // БЕСПЛАТНО // 📱Online » Компьютеры и Интернет » Программирование » Системное программирование в среде Windows - Джонсон Харт

Читаем без скачивания Системное программирование в среде Windows - Джонсон Харт

Читать онлайн Системное программирование в среде Windows - Джонсон Харт

Шрифт:

-
+

Интервал:

-
+

Закладка:

Сделать
1 ... 88 89 90 91 92 93 94 95 96 ... 142
Перейти на страницу:

 TCHAR TmpFileName[MAX_PATH]; /* Имя временного файла. */

} THREAD_ARG;

typedef THREAD_ARG *LPTHREAD_ARG;

volatile static BOOL ShutDown = FALSE;

static DWORD WINAPI Server(LPTHREAD_ARG);

static DWORD WINAPI Connect(LPTHREAD_ARG);

static DWORD WINAPI ServerBroadcast(LPLONG);

static BOOL WINAPI Handler(DWORD);

static TCHAR ShutRqst[] = _T("$ShutDownServer");

_tmain(int argc, LPTSTR argv[]) {

 /* Определение MAX_CLIENTS содержится в файле ClntSrvr.h. */

 HANDLE hNp, hMonitor, hSrvrThread[MAXCLIENTS];

 DWORD iNp, MonitorId, ThreadId;

 LPSECURITY_ATTRIBUTES pNPSA = NULL;

 THREAD_ARG ThArgs[MAXCLIENTS];

 /* Обработчик управляющих сигналов консоли, используемый для остановки сервера. */

 SetConsoleCtrlHandler(Handler, TRUE);

 /* Периодически создавать имя широковещательного канала потока. */

 hMonitor = (HANDLE)_beginthreadex(NULL, 0, ServerBroadcast, NULL, 0, &MonitorId);

 /* Создать экземпляр канала и временный файл для каждого серверного потока. */

 for (iNp = 0; iNp < MAX_CLIENTS; iNp++) {

  hNp = CreateNamedPipe(SERVER_PIPE, PIPE_ACCESS_DUPLEX, PIPE_READMODE_MESSAGE | PIPE_TYPE_MESSAGE | PIPE_WAIT, MAXCLIENTS, 0, 0, INFINITE, pNPSA);

  ThArgs[iNp].hNamedPipe = hNp;

  ThArgs[iNp].ThreadNo = iNp;

  GetTempFileName(_T("."), _T("CLP"), 0, ThArgs[iNp].TmpFileName);

  hSrvrThread[iNp] = (HANDLE)_beginthreadex(NULL, 0, Server, &ThArgs[iNp], 0, &ThreadId);

 }

 /* Ждать завершения выполнения всех потоков. */

 WaitForMultipleObjects(MAXCLIENTS, hSrvrThread, TRUE, INFINITE);

 WaitForSingleObject(hMonitor, INFINITE);

 CloseHandle(hMonitor);

 for (iNp = 0; iNp < MAXCLIENTS; iNp++) {

  /* Закрыть дескрипторы канала и удалить временные файлы. */

  CloseHandle(hSrvrThread[iNp]);

  DeleteFile(ThArgs[iNp].TmpFileName);

 }

 _tprintf(_T("Серверный процесс завершил выполнение.n"));

 return 0;

}

static DWORD WINAPI Server(LPTHREAD_ARG pThArg)

/* Функция потока сервера; по одной для каждого потенциального клиента. */

{

 HANDLE hNamedPipe, hTmpFile = INVALID_HANDLE_VALUE, hConTh, hClient;

 DWORD nXfer, ConThId, ConThStatus;

 STARTUPINFO StartInfoCh;

 SECURITY_ATTRIBUTES TempSA = {sizeof(SECURITY_ATTRIBUTES), NULL, TRUE};

 PROCESS_INFORMATION ProcInfo;

 FILE *fp;

 REQUEST Request;

 RESPONSE Response;

 GetStartupInfo(&StartInfoCh);

 hNamedPipe = pThArg->hNamedPipe;

 hTmpFile = CreateFile(pThArg->TmpFileName, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, &TempSA, CREATE_ALWAYS, FILE_ATTRIBUTE_TEMPORARY, NULL);

 while (!ShutDown) { /* Цикл соединений. */

  /* Создать поток соединения; ждать его завершения. */

  hConTh = (HANDLE)_beginthreadex(NULL, 0, Connect, pThArg, 0, &ConThId);

  /* Ожидание соединения с клиентом и проверка флага завершения работы.*/

  while (!ShutDown && WaitForSingleObject(hConTh, CS_TIMEOUT) == WAIT_TIMEOUT) { /* Пустое тело цикла. */ }; 

  CloseHandle(hConTh);

  if (ShutDown) continue; /*Флаг может быть установлен любым потоком.*/

  /* Соединение существует. */

  while (!ShutDown && ReadFile(hNamedPipe, &Request, RQ_SIZE, &nXfer, NULL)) {

   /* Получать новые команды до отсоединения клиента. */

   ShutDown = ShutDown || (_tcscmp(Request.Record, ShutRqst) == 0);

   if (ShutDown) continue; /* Проверяется на каждой итерации. */

   /* Создать процесс для выполнения команды. */

   StartInfoCh.hStdOutput = hTmpFile;

   StartInfoCh.hStdError = hTmpFile;

   StartInfoCh.hStdInput = GetStdHandle(STD_INPUT_HANDLE);

   StartInfoCh.dwFlags = STARTF_USESTDHANDLES;

   CreateProcess(NULL, Request.Record, NULL, NULL, TRUE, /* Унаследовать дескрипторы. */

    0, NULL, NULL, &StartInfoCh, &ProcInfo);

   /* Выполняется процесс сервера. */

   CloseHandle(ProcInfo.hThread);

   WaitForSingleObject(ProcInfo.hProcess, INFINITE);

   CloseHandle(ProcInfo.hProcess);

   /* Отвечать по одной строке за один раз. Здесь удобно использовать функции библиотеки С для работы со строками. */

   fp = _tfopen(pThArg->TmpFileName, _T("r"));

   Response.Status = 0;

   while(_fgetts(Response.Record, MAX_RQRS_LEN, fp) != NULL) WriteFile(hNamedPipe, &Response, RS_SIZE, &nXfer, NULL);

   FlushFileBuffers(hNamedPipe);

   fclose(fp);

   /* Уничтожить содержимое временного файла. */

   SetFilePointer(hTmpFile, 0, NULL, FILE_BEGIN);

   SetEndOfFile(hTmpFile);

   /* Отправить признак конца ответа. */

   Response.Status = 1;

   strcpy(Response.Record, "");

   WriteFile(hNamedPipe, &Response, RS_SIZE, &nXfer, NULL);

  }

  /* Конец основного командного цикла. Получить следующую команду. */

  /* Принудительно завершить выполнение потока, если он все еще активен.*/

  GetExitCodeThread(hConTh, &ConThStatus);

  if (ConThStatus == STILL_ACTIVE) {

   hClient = CreateFile(SERVER_PIPE, GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN EXISTING, FILE ATTRIBUTE NORMAL, NULL); 

   if (hClient != INVALID_HANDLE_VALUE) CloseHandle (hClient);

   WaitForSingleObject (hConTh, INFINITE);

  }

  /* Клиент отсоединился или имеется запрос останова. */

  FlushFileBuffers(hNamedPipe);

  DisconnectNamedPipe(hNamedPipe);

 }

 /* Конец командного цикла. Освободить ресурсы; выйти из потока. */

 if (hTmpFile != INVALID_HANDLE_VALUE) CloseHandle(hTmpFile);

 DeleteFile(pThArg->TmpFileName);

 _tprintf(_T("Выход из потока номер %dn"), pThArg->ThreadNo);

 _endthreadex(0);

}

static DWORD WINAPI Connect(LPTHREAD_ARG pThArg) {

 /* Поток соединения разрешает серверу опрос флага ShutDown. */

 ConnectNamedPipe(pThArg->hNamedPipe, NULL);

 _endthreadex(0);

 return 0;

}

BOOL WINAPI Handler(DWORD CtrlEvent) {

 /* Завершить работу системы. */

 ShutDown = TRUE;

 return TRUE;

}

Комментарии по поводу клиент-серверного процессора командной строки

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

• Соединяться с сервером и выполнять параллельные запросы могут сразу несколько серверов; каждому клиенту назначается серверный (или рабочий) поток, выделяемый из пула потоков.

• Сервер и клиенты могут выполняться либо в ответ на отдельные подсказки командной строки, либо под управлением программы JobShell (программа 6.3).

• Если во время попыток клиента соединиться с сервером все экземпляры именованного канала оказываются задействованными, то новый клиент будет находиться в состоянии ожидания до тех пор, пока другой клиент не разорвет соединение в ответ на получение команды $Quit, тем самым делая его доступным для ожидающего клиента. Возможны ситуации, когда сразу несколько новых клиентов будут одновременно пытаться создать соединение с сервером, соревнуясь между собой за право открытия доступного экземпляра; потоки, проигравшие в этой конкурентной борьбе, будут вынуждены вновь перейти в состояние ожидания.

• Каждый серверный поток выполняет синхронные операции ввода/вывода, но одни из этих потоков могут обрабатывать запросы, в то время как другие — ожидать соединения или поступления клиентских запросов.

• С учетом ограничений, свойственных именованным каналам, о чем говорилось ранее в этой главе, расширение программы на случай сетевых клиентов не составляет труда. Для этого достаточно заменить имена каналов в заголовочном файле или добавить параметр, указывающий имя сервера в командной строке клиента.

• Каждый рабочий поток сервера создает простой поток, осуществляющий соединение, который вызывает функцию ConnectNamedPipe и завершает выполнение сразу же после подключения клиента. Это позволяет организовать ожидание дескриптора потока соединения рабочим потоком с использованием конечного интервала ожидания и периодическое тестирование глобального флага завершения работы (ShutDown). Если бы рабочие потоки блокировались при выполнении функции ConnectNamedPipe, они не могли бы тестировать этот флаг, и сервер не мог бы завершить работу. По этой причине поток сервера осуществляет вызов CreateFile, используя дескриптор именованного канала, чтобы заставить поток соединения возобновиться и завершить выполнение. Альтернативным вариантом было бы использование асинхронного ввода/вывода (глава 14), что дало бы возможность связать событие с вызовом функции ConnectNamedPipe. Другие возможные варианты реализации и дополнительная информация предоставляются в комментариях к исходному тексту программы, размещенному на Web-сайте книги. Без этого решения потоки соединения могли бы никогда не завершить работу самостоятельно, что привело бы к утечке ресурсов в DLL. Этот вопрос обсуждается в главе 12.

• Существует ряд благоприятных предпосылок для усовершенствования данной системы. Например, можно предусмотреть опцию выполнения внутрипроцессного сервера (in-process server), используя библиотеку DLL, которая реализует некоторые из команд. Это усовершенствование вводится в программу в главе 12.

• Количество серверных потоков ограничивается при вызове функции WaitForMultipleObjects в основном потоке. Хотя это ограничение легко преодолимо, в данном случае система не обладает истинной масштабируемостью; как было показано в главе 10, чрезмерное увеличение количества потоков может оказать отрицательное влияние на производительность. В главе 14 для решения этой проблемы используются порты асинхронного ввода/вывода.

1 ... 88 89 90 91 92 93 94 95 96 ... 142
Перейти на страницу:
На этой странице вы можете бесплатно скачать Системное программирование в среде Windows - Джонсон Харт торрент бесплатно.
Комментарии