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

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

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

Шрифт:

-
+

Интервал:

-
+

Закладка:

Сделать
1 ... 95 96 97 98 99 100 101 102 103 ... 142
Перейти на страницу:

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

• Данный сервер поддерживает также внутрипроцессные серверы (in-process servers), что достигается путем загрузки библиотеки DLL во время инициализации. Имя библиотеки DLL задается в командной строке, и серверный поток сначала пытается определить точку входа этой DLL. В случае успеха серверный поток вызывает точку входа DLL; в противном случае сервер создает процесс аналогично тому, как это делалось в программе serverNP. Пример DLL приведен в программе 12.3. Поскольку генерация исключений библиотекой DLL будет приводить к уничтожению всего серверного процесса, вызов функции DLL защищен простым обработчиком исключений.

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

Поскольку в коде сервера использованы специфические для Windows возможности, в частности, возможности управления потоками и некоторые другие, он, в отличие от кода клиента, оказывается привязанным к Windows.

Программа 12.2. serverSK: сервер на основе сокета с внутрипроцессными серверами 

/* Глава 12. Клиент-серверная система. ПРОГРАММА СЕРВЕРА. ВЕРСИЯ НА ОСНОВЕ СОКЕТА. */

/* Выполняет указанную в запросе команду и возвращает ответ. */

/* Если удается обнаружить точку входа разделяемой библиотеки, команды */

/* выполняются внутри процесса, в противном случае – вне процесса. */

/* ДОПОЛНИТЕЛЬНАЯ ВОЗМОЖНОСТЬ: argv [1] может содержать имя библиотеки */

/* DLL, поддерживающей внутрипроцессные серверы. */

#define _NOEXCLUSIONS

#include "EvryThng.h"

#include "ClntSrvr.h" /* Определяет структуру записей запроса и ответа. */

struct sockaddr_in SrvSAddr;

/* Адресная структура сокета сервера. */

struct sockaddr_in ConnectSAddr; /* Подключенный сокет. */

WSADATA WSStartData; /* Структура данных библиотеки сокета. */

typedef struct SERVER_ARG_TAG { /* Аргументы серверного потока. */

 volatile DWORD number;

 volatile SOCKET sock;

 volatile DWORD status;

 /* Пояснения содержатся в комментариях к основному потоку. */

 volatile HANDLE srv_thd;

 HINSTANCE dlhandle; /* Дескриптор разделяемой библиотеки. */

} SERVER_ARG;

volatile static ShutFlag = FALSE;

static SOCKET SrvSock, ConnectSock;

int _tmain(DWORD argc, LPCTSTR argv[]) {

 /* Прослушивающий и подключенный сокеты сервера. */

 BOOL Done = FALSE;

 DWORD ith, tstatus, ThId;

 SERVER_ARG srv_arg[MAX_CLIENTS];

 HANDLE hAcceptTh = NULL;

 HINSTANCE hDll = NULL;  

 /* Инициализировать библиотеку WSA; задана версия 2.0, но будет работать и версия 1.1. */

 WSAStartup(MAKEWORD(2, 0), &WSStartData);

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

 if (argc > 1) hDll = LoadLibrary(argv[1]);

 /* Инициализировать массив arg потока. */

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

  srv_arg[ith].number = ith;

  srv_arg[ith].status = 0;

  srv_arg[ith].sock = 0;

  srv_arg[ith].dlhandle = hDll;

  srv_arg[ith].srv_thd = NULL;

 }

 /* Следовать стандартной процедуре вызова последовательности функций socket/bind/listen/accept клиентом. */

 SrvSock = socket(AF_INET, SOCK_STREAM, 0);

 SrvSAddr.sin_family = AF_INET;

 SrvSAddr.sin_addr.s_addr = htonl(INADDR_ANY);

 SrvSAddr.sin_port = htons(SERVER_PORT);

 bind(SrvSock, (struct sockaddr *)&SrvSAddr, sizeof SrvSAddr);

 listen(SrvSock, MAX_CLIENTS);

 /* Основной поток становится потоком прослушивания/соединения/контроля.*/

 /* Найти пустую ячейку в массиве arg потока сервера. */

 /* параметр состояния: 0 – ячейка свободна; 1 – поток остановлен; 2 — поток выполняется; 3 – остановлена вся система. */

 while (!ShutFlag) {

  for (ith = 0; ith < MAX_CLIENTS && !ShutFlag; ) {

   if (srv_arg[ith].status==1 || srv_arg[ith].status==3) { /* Выполнение потока завершено либо обычным способом, либо по запросу останова. */

    WaitForSingleObject(srv_arg[ith].srv_thd INFINITE);

    CloseHandle(srv_arg[ith].srv_tnd);

    if (srv_arg[ith].status == 3) ShutFlag = TRUE;

    else srv_arg[ith].status = 0;

    /* Освободить ячейку данного потока. */

   }

   if (srv_arg[ith].status == 0 || ShutFlag) break;

   ith = (ith + 1) % MAXCLIENTS;

   if (ith == 0) Sleep(1000);

   /* Прервать цикл опроса. */

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

  }

  /* Ожидать попытки соединения через данный сокет. */

  /* Отдельный поток для опроса флага завершения ShutFlag. */

  hAcceptTh = (HANDLE)_beginthreadex(NULL, 0, AcceptTh, &srv_arg[ith], 0, &ThId); 

  while (!ShutFlag) {

   tstatus = WaitForSingleObject(hAcceptTh, CS_TIMEOUT);

   if (tstatus == WAIT_OBJECT_0) break; /* Соединение установлено. */

  }

  CloseHandle(hAcceptTh);

  hAcceptTh = NULL; /* Подготовиться к следующему соединению. */

 }

 _tprintf(_T("Остановка сервера. Ожидание завершения всех потоков сервераn"));

 /* Завершить принимающий поток, если он все еще выполняется. */

 /* Более подробная информация об используемой логике завершения */

 /* работы приведена на Web-сайте книги. */

 if (hDll != NULL) FreeLibrary(hDll);

 if (hAcceptTh != NULL) TerminateThread(hAcceptTh, 0);

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

 for (ith = 0; ith < MAXCLIENTS; ith++) if (srv_arg [ith].status != 0) {

  WaitForSingleObject(srv_arg[ith].srv_thd, INFINITE);

  CloseHandle(srv_arg[ith].srv_thd);

 }

 shutdown(SrvSock, 2);

 closesocket(SrvSock);

 WSACleanup();

 return 0;

}

static DWORD WINAPI AcceptTh(SERVER_ARG * pThArg) {

 /* Принимающий поток, который предоставляет основному потоку возможность опроса флага завершения. Кроме того, этот поток создает серверный поток. */

 LONG AddrLen, ThId;

 AddrLen = sizeof(ConnectSAddr);

 pThArg->sock = accept(SrvSock, /* Это блокирующий вызов. */

  (struct sockaddr *)&ConnectSAddr, &AddrLen);

 /* Новое соединение. Создать серверный поток. */

 pThArg->status = 2;

 pThArg->srv_thd = (HANDLE)_beginthreadex (NULL, 0, Server, pThArg, 0, &ThId);

 return 0; /* Серверный поток продолжает выполняться. */

}

static DWORD WINAPI Server(SERVER_ARG * pThArg)

/* Функция серверного потока. Поток создается по требованию. */

{

 /* Каждый поток поддерживает в стеке собственные структуры данных запроса, ответа и регистрационных записей. */

 /* … Стандартные объявления из serverNP опущены … */

 SOCKET ConnectSock; 

 int Disconnect = 0, i;

 int (*dl_addr)(char *, char *);

 char *ws = " tn"; /* Пробелы. */

 GetStartupInfo(&StartInfoCh);

 ConnectSock = pThArg->sock;

 /* Создать имя временного файла. */

 sprintf(TempFile, "%s%d%s", "ServerTemp", pThArg->number, ".tmp");

 while (!Done && !ShutFlag) { /* Основной командный цикл. */

  Disconnect = ReceiveRequestMessage(&Request, ConnectSock);

  Done = Disconnect || (strcmp(Request.Record, "$Quit") == 0) || (strcmp(Request.Record, "$ShutDownServer") == 0);

  if (Done) continue;

  /* Остановить этот поток по получении команды "$Quit" или "$ShutDownServer". */

  hTrapFile = CreateFile(TempFile, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, &TempSA, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);

  /* Проверка наличия этой команды в DLL. Для упрощения команды */

  /* разделяемой библиотеки имеют более высокий приоритет по сравнению */

  /* с командами процесса. Прежде всего, необходимо извлечь имя команды.*/

  i = strcspn(Request.Record, ws); /* Размер лексемы. */

  memcpy(sys_command, Request.Record, i) ;

  sys_command[i] = '';

  dl_addr = NULL; /* Будет установлен в случае успешного выполнения функции GetProcAddress. */

  if (pThArg->dlhandle != NULL) {/* Проверка поддержки "внутрипроцессного" сервера. */

   dl_addr = (int (*)(char *, char *))GetProcAddress(pThArg->dlhandle, sys_command);

   if (dl_addr != NULL) __try {

    /* Защитить серверный процесс от исключений, возникающих в DLL*/

    (*dl_addr)(Request.Record, TempFile);

   } __except (EXCEPTION_EXECUTE_HANDLER) {

    ReportError(_T("Исключение в DLL"), 0, FALSE);

   }

  }

  if (dl_addr == NULL) { /* Поддержка внутрипроцессного сервера отсутствует. */

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

   /* … То же, что в serverNP … */

  }

  /* … То же, что в serverNP … */

1 ... 95 96 97 98 99 100 101 102 103 ... 142
Перейти на страницу:
На этой странице вы можете бесплатно скачать Системное программирование в среде Windows - Джонсон Харт торрент бесплатно.
Комментарии