- Любовные романы
- Фантастика и фэнтези
- Ненаучная фантастика
- Ироническое фэнтези
- Научная Фантастика
- Фэнтези
- Ужасы и Мистика
- Боевая фантастика
- Альтернативная история
- Космическая фантастика
- Попаданцы
- Юмористическая фантастика
- Героическая фантастика
- Детективная фантастика
- Социально-психологическая
- Боевое фэнтези
- Русское фэнтези
- Киберпанк
- Романтическая фантастика
- Городская фантастика
- Технофэнтези
- Мистика
- Разная фантастика
- Иностранное фэнтези
- Историческое фэнтези
- LitRPG
- Эпическая фантастика
- Зарубежная фантастика
- Городское фентези
- Космоопера
- Разное фэнтези
- Книги магов
- Любовное фэнтези
- Постапокалипсис
- Бизнес
- Историческая фантастика
- Социально-философская фантастика
- Сказочная фантастика
- Стимпанк
- Романтическое фэнтези
- Ироническая фантастика
- Детективы и Триллеры
- Проза
- Юмор
- Феерия
- Новелла
- Русская классическая проза
- Современная проза
- Повести
- Контркультура
- Русская современная проза
- Историческая проза
- Проза
- Классическая проза
- Советская классическая проза
- О войне
- Зарубежная современная проза
- Рассказы
- Зарубежная классика
- Очерки
- Антисоветская литература
- Магический реализм
- Разное
- Сентиментальная проза
- Афоризмы
- Эссе
- Эпистолярная проза
- Семейный роман/Семейная сага
- Поэзия, Драматургия
- Приключения
- Детская литература
- Загадки
- Книга-игра
- Детская проза
- Детские приключения
- Сказка
- Прочая детская литература
- Детская фантастика
- Детские стихи
- Детская образовательная литература
- Детские остросюжетные
- Учебная литература
- Зарубежные детские книги
- Детский фольклор
- Буквари
- Книги для подростков
- Школьные учебники
- Внеклассное чтение
- Книги для дошкольников
- Детская познавательная и развивающая литература
- Детские детективы
- Домоводство, Дом и семья
- Юмор
- Документальные книги
- Бизнес
- Работа с клиентами
- Тайм-менеджмент
- Кадровый менеджмент
- Экономика
- Менеджмент и кадры
- Управление, подбор персонала
- О бизнесе популярно
- Интернет-бизнес
- Личные финансы
- Делопроизводство, офис
- Маркетинг, PR, реклама
- Поиск работы
- Бизнес
- Банковское дело
- Малый бизнес
- Ценные бумаги и инвестиции
- Краткое содержание
- Бухучет и аудит
- Ораторское искусство / риторика
- Корпоративная культура, бизнес
- Финансы
- Государственное и муниципальное управление
- Менеджмент
- Зарубежная деловая литература
- Продажи
- Переговоры
- Личная эффективность
- Торговля
- Научные и научно-популярные книги
- Биофизика
- География
- Экология
- Биохимия
- Рефераты
- Культурология
- Техническая литература
- История
- Психология
- Медицина
- Прочая научная литература
- Юриспруденция
- Биология
- Политика
- Литературоведение
- Религиоведение
- Научпоп
- Психология, личное
- Математика
- Психотерапия
- Социология
- Воспитание детей, педагогика
- Языкознание
- Беременность, ожидание детей
- Транспорт, военная техника
- Детская психология
- Науки: разное
- Педагогика
- Зарубежная психология
- Иностранные языки
- Филология
- Радиотехника
- Деловая литература
- Физика
- Альтернативная медицина
- Химия
- Государство и право
- Обществознание
- Образовательная литература
- Учебники
- Зоология
- Архитектура
- Науки о космосе
- Ботаника
- Астрология
- Ветеринария
- История Европы
- География
- Зарубежная публицистика
- О животных
- Шпаргалки
- Разная литература
- Зарубежная литература о культуре и искусстве
- Пословицы, поговорки
- Боевые искусства
- Прочее
- Периодические издания
- Фанфик
- Военное
- Цитаты из афоризмов
- Гиды, путеводители
- Литература 19 века
- Зарубежная образовательная литература
- Военная история
- Кино
- Современная литература
- Военная техника, оружие
- Культура и искусство
- Музыка, музыканты
- Газеты и журналы
- Современная зарубежная литература
- Визуальные искусства
- Отраслевые издания
- Шахматы
- Недвижимость
- Великолепные истории
- Музыка, танцы
- Авто и ПДД
- Изобразительное искусство, фотография
- Истории из жизни
- Готические новеллы
- Начинающие авторы
- Спецслужбы
- Подростковая литература
- Зарубежная прикладная литература
- Религия и духовность
- Старинная литература
- Справочная литература
- Компьютеры и Интернет
- Блог
О чём не пишут в книгах по Delphi - А. Григорьев
Шрифт:
Интервал:
Закладка:
// приведение типов вполне возможно, хотя и некрасиво
S := TSocket(Parameter);
// Связываем событие SockEvents[0] с FD_READ и FD_CLOSE
WSAEventSelect(S, SockEvents[0], FD_READ or FD_CLOSE);
while True do
begin
case WSAWaitForMultipleEvents(3, @SockEvents[0], True, WSA_INFINITE, False) of
WSA_WAIT_EVENT_0: begin
WSAEnumNetworkEvents(S, SockEvents[0], NetworkEvents);
if NetworkEvents.lNetworkEvents and FD_READ > 0 then
if NetworkEvents.iErrorCode[FD_READ_BIT] = 0 then
begin
// Пришли данные, которые нужно прочитать
end
else
begin
// произошла ошибка. Нужно сообщить о ней и завершить нить
closesocket(3);
Exit;
end;
if NetworkEvents.lNetworkEvents and FD_CLOSE > 0 then
begin
// Связь разорвана
if NetworkEvents.iErrorCode[FD_CLOSE_BIT] = 0 then begin
// Связь закрыта корректно
end
else
begin
// Связь разорвана в результате сбоя сети
end;
// В любом случае нужно закрыть сокет и завершить нить
closesocket(S);
Exit;
end;
end;
WSA_WAIT_EVENT_0 + 1: begin
// Получен сигнал о необходимости отправить данные
// Здесь должен быть код отправки данных
// После отправки событие нужно сбросить вручную
ResetEvent(SockEvents[1]);
end;
WSA_WAIT_EVENT_0 + 2: begin
// Получен сигнал о необходимости завершения работы нити
closesocket;
ResetEvents(SockEvents[2]);
Exit;
end
end;
end;
end;
Как и во всех предыдущих примерах, здесь для краткости не проверяются результаты, возвращаемые функциями и не отлавливаются возникающие ошибки. Кроме того, отсутствует процедура завершения связи с вызовом shutdown.
Данный пример может рассматриваться как фрагмент кода простого сервера. В отдельной нити такого сервера выполняется цикл, состоящий из вызова accept и создания новой нити для обслуживания полученного таким образом сокета. Затем другие нити при необходимости могут давать таким нитям команды (необходимо только предусмотреть для каждой нити, обслуживающей сокет, свою копию массива SockEvents). Благодаря этому каждый клиент будет обслуживаться независимо.
К недостаткам такого сервера следует отнести его низкую устойчивость против DoS-атак, при которых к серверу подключается очень большое число клиентов. Если сервер будет создавать отдельную нить для обслуживания каждого подключения, количество нитей очень быстро станет слишком большим, и вся система окажется неработоспособной, т.к. большая часть процессорного времени будет тратиться на переключение между нитями. Более защищенным является вариант, при котором сервер заранее создает некоторое разумное количество нитей (пул нитей) и обработку запроса или выполнение команды поручает любой свободной нити из этого пула. Если ни одной свободной нити в пуле нет, задание ставится в очередь. По мере освобождения нитей задания извлекаются из очереди и выполняются. При DoS-атаках такой сервер также не справляется с поступающими заданиями, но это не приводит к краху всей системы. Но сервер с пулом нитей реализуется сложнее (обычно — через порты завершения, которые мы здесь не рассматриваем). Тем не менее простой для реализации сервер без пула нитей тоже может оказаться полезным, если вероятность DoS-атак низка (например, в изолированных технологических подсетях).
Приведенный пример может рассматриваться также как заготовка для клиента. В этом случае целесообразнее передавать в функцию ProcessSockEvents не готовый сокет, а только адрес сервера, к которому необходимо подключиться. Создание сокета и установление связи с сервером при этом выполняет сама нить перед началом цикла ожидания событий. Такой подход очень удобен для независимой работы с несколькими однотипными серверами.
2.2.8. Пример использования сокетов с событиями
К достоинствам асинхронного режима, основанного на сообщениях, относится то, что нить, обслуживающая сокет, может выходить из состояния ожидания не только при получении данных сокетом, но и по иным сигналам. Протокол обмена который мы до сих пор использовали, не позволяет продемонстрировать это достоинство в полном объеме. Поэтому, прежде чем создавать пример, мы несколько изменим протокол. Формат пакетов оставим прежним, изменим условия, при которых эти пакеты могут быть посланы. Теперь клиент имеет право посылать пакеты, не дожидаясь от сервера ответа на предыдущий пакет, а сервер имеет право посылать пакеты клиенту не только в ответ на его запросы, но и по собственной инициативе. В нашей реализации он будет посылать клиентам строку с текущим временем.
Сервер на асинхронных событиях (пример EventSelectServer на компакт-диске) имеет много общего с рассмотренным ранее многонитевым сервером (пример MultithreadedServer, см. разд. 2.1.12). В нем также есть нить для обработки подключений клиентов и по одной нити на каждого клиента, а главная нить только создает слушающий сокет и запускает обслуживающую его нить.
Еще одним важным отличием нашего сервера от всех предыдущих примеров серверов станет то, что пользователь сможет его остановить в любой момент. Подобную функциональность было бы несложно добавить и к таким серверам, как SelectServer, NonBlockingServer и AsyncSelectServer, которые работают в одной нити. Но остановить нити в многонитевом сервере можно было только одним способом: уничтожив сокеты из главной нити — в этом случае все работающие с этими сокетами нити завершились бы с ошибками. Очевидно, что это порочный подход, не позволяющий корректно завершить работу с клиентами. Режим с использованием событий позволяет предусмотреть реакцию нити на внешний сигнал об отключении. Отключаться сервер будет по нажатию кнопки Остановить.
В листинге 2.63 приведен код нити, взаимодействующей с клиентом (код методов LogMessage и DoLogMessage опущен, т.к. он идентичен приведенному в листингах 2.20 и 2.7 соответственно).
Листинг 2.63. Нить, взаимодействующая с клиентамиunit ClientThread;
{
Нить, обслуживающая одного клиента.
Выполняет цикл, выход из которого возможен по внешнему сигналу или при возникновении ошибки на сокете. Умеет отправлять клиенту сообщения по внешнему сигналу.
}
interface
uses
Windows, Classes, WinSock, Winsock2_Events, ShutdownConst, SysUtils, SyncObjs;
type
TClientThread = class(TThread)
private
// Сообщение, которое нужно добавить в лог,
// хранится в отдельном поле, т.к. метод, вызывающийся через
// Synchronize, не может иметь параметров.
FMessage: string;
// Префикс для всех сообщений лога, связанных с данным клиентом
FHeader: string;
// Сокет для взаимодействия с клиентом
FSocket: TSocket;
// События нити
// FEvents[0] используется для остановки нити
// FEvents[1] используется для отправки сообщения
// FEvents[2] связывается с событиями FD_READ, FD_WRITE и FD_CLOSE
FEvents; array[0..2] of TWSAEvent;
// Критическая секция для доступа к буферу исходящих
FSendBufSection: TCriticalSection;
// Буфер исходящих
FSendBuf: string;
// Вспомогательный метод для вызова через Synchronize
procedure DoLogMessage;
// Функция, проверяющая, завершила ли нить работу
function GetFinished: Boolean;
protected
procedure Execute; override;
// Вывод сообщения в лог главной формы
procedure LogMessage(сonst Msg: string);
// Отправка клиенту данных из буфера исходящих
function DoSendBuf: Boolean;
public
constructor Create(ClientSocket: TSocket; const ClientAddr: TSockAddr);
destructor Destroy; override;
// Добавление строки в буфер исходящих
procedure SendString(const S: string);
// Остановка нити извне
procedure StopThread;
property Finished: Boolean read GetFinished;
end;
ESocketError = class(Exception);
implementation
uses
MainServerUnit;
{ TClientThread }
// Сокет для взаимодействия с клиентом создается в главной нити,
// а сюда передается через параметр конструктора. Для формирования
// заголовка сюда же передается адрес подключившегося клиента
constructor TClientThread.Create(ClientSocket: TSocket; const ClientAddr: TSockAddr);

