Чтение онлайн

на главную - закладки

Жанры

О чём не пишут в книгах по Delphi

Григорьев Антон Борисович

Шрифт:

GetErrorString);

Break;

end;

else

// Неожиданный результат при ожидании

begin

LogMessage(

'Внутренняя ошибка сервера — неожиданный результат ожидания '

+ IntToStr(WaitRes));

Break;

end;

end;

 until False;

 // Останавливаем и уничтожаем все нити клиентов

 for I := 0 to FClientThreads.Count - 1 do

 begin

TClientThread(FClientThreads[I]).StopThread;

TClientThread(FClientThreads[I]).WaitFor;

TClientThread(FClientThreads[I]).Free;

 end;

 closesocket(FServerSocket);

 LogMessage('Сервер
завершил работу');

 Synchronize(ServerForm.OnStopServer);

end;

// Завершение работы сервера. Просто взводим соответствующее

// событие, а остальное делает код в методе Execute.

procedure TListenThread.StopServer;

begin

 WSASetEvent(FEvents[0));

end;

end.

Нить

TListenThread
реализует сразу несколько функций. Во-первых, она обслуживает подключение клиентов и создает нити для их обслуживания. Во-вторых, уничтожает объекты завершившихся нитей. В-третьих, она с определённой периодичностью ставит в очередь на отправку всем клиентам сообщение с текущим временем сервера. И в-четвертых, управляет уничтожением клиентских нитей при завершении работы сервера.

Здесь следует пояснить, почему выбран такой способ управления временем жизни объектов клиентских нитей. Очевидно, что нужно иметь список всех нитей, чтобы обеспечить возможность останавливать их и ставить в очередь сообщения для отправки клиентам (этот список реализован переменной

FClientThreads
). Если бы объект
TClientThread
автоматически удалялся при завершении работы нити, в его деструкторе пришлось бы предусмотреть и удаление ссылки на объект из списка, а это значит, что к списку пришлось бы обращаться из разных нитей. Соответственно, потребовалось бы синхронизировать обращение к списку, и здесь мы бы столкнулись с одной неприятной проблемой. Когда нить
TListenThread
получает команду завершиться, она должна завершить все клиентские нити. Для этого она должна использовать их список для отправки сигнала и ожидания их завершения. И получилась бы взаимная блокировка, потому что нить
TListenThread
ждала бы завершения клиентских нитей, а они не могли бы завершиться, потому что им требовался бы список, захваченный нитью
TListenThread
. Избежать этого можно с помощью асинхронных сообщений, но в нашем случае реализация этого механизма затруднительна (хотя и возможна). Для простоты был выбран другой вариант: клиентские нити сами свои объекты не удаляют, а к списку имеет доступ только нить
TListenThread
, которая время от времени проходит по по списку и удаляет объекты всех завершившихся нитей. В этом случае клиентские нити не используют синхронизацию при завершении, и нить TListenThread может дожидаться их.

Нить

TListenThread
использует два события:
FEvents[0]
для получения сигнала о необходимости закрытия и
FEvents[1]
для получения уведомлений о возникновении события
FD_ACCEPT
на слушающем сокете (т.е. о подключении клиента). Порядок следования событий
к массиве определяется теми же соображениями, что и в случае клиентской нити: сигнал остановки нити должен иметь более высокий приоритет. чтобы в случае DoS-атаки нить могла быть остановлена.

И поиск завершившихся нитей, и отправка сообщений с текущим временем клиентам осуществляется в том случае, если при ожидании события произошёл тайм-аут (который в нашем случае равен 15 c). Подключение клиента — событие достаточно редкое, поэтому такое решение выгладит вполне оправданным. Для тех экзотических случаев, когда клиенты часто подключаются и отключаются, можно предусмотреть еще одно событие у нити

TListenThread
, при наступлении которого она будет проверять список клиентов. Клиентская нить при своем завершении будет взводить это событие. Что же касается отправки сообщений клиентам, то в обработчик тайм-аута этот код помещён в демонстрационных целях. В реальной программе инициировать отправку сообщений клиентам будет, скорее всего, другой код, например, код главной нити по команде пользователя.

Несмотря на изменение протокола, новый сервер был бы вполне совместим со старым клиентом SimpleClient (см. разд. 2.1.11), если бы не отправлял сообщения по своей инициативе. Действительно, прочие изменения в протоколе разрешают клиенту отправлять новые сообщения до получения ответа сервера, но не обязывают его делать это. В класс

TClientThread
добавлено логическое поле
FServerMsg
. Если оно равно
False
, то сервер не посылает клиентам сообщений по собственной инициативе, т.е. работает в режиме совместимости со старым клиентом. Поле
FServerMsg
инициализируется в соответствии с параметром, переданным в конструктор, т.е. в соответствии с состоянием галочки Сообщения от сервера, расположенной на главной форме. Если перед запуском сервера она снята, сервер не будет сам посылать сообщения, и старый клиент сможет обмениваться данными с ним.

Запуск сервера практически не отличается от запуска сервера MultithreadedServer (см. листинг 2.19), только теперь объект, созданный конструктором, запоминается классом главной формы, чтобы потом можно было сервер остановить. Остановка осуществляется методом

StopServer
(листинг 2.65).

Листинг 2.65. Метод
StopServer

// Остановка сервера

procedure TServerForm.StopServer;

begin

 // Запрещаем кнопку, чтобы пользователь не мог нажать ее

 // еще раз, пока сервер не остановится.

 BtnStopServer.Enabled := False;

 // Ожидаем завершения слушавшей нити. Так как вывод сообщений

 // эта нить осуществляет через Synchronize, выполняемый главной

 // нитью в петле сообщений, вызов метода WaitFor мог бы привести

 // к взаимной блокировке: главная нить ждала бы, когда завершится

 // нить TListenThread, а та, в свою очередь - когда главная нить

 // выполнит Synchronize. Чтобы этого не происходило, организуется

 // ожидание с локальной петлей сообщений.

 if Assigned(FListenThread) then

 begin

FListenThread.StopServer;

while Assigned(FListenThread) do

begin

Application.ProcessMessages;

Sleep(10);

end;

 end;

Поделиться:
Популярные книги

Кодекс Крови. Книга III

Борзых М.
3. РОС: Кодекс Крови
Фантастика:
фэнтези
попаданцы
аниме
5.00
рейтинг книги
Кодекс Крови. Книга III

Мастер темных Арканов

Карелин Сергей Витальевич
1. Мастер темных арканов
Фантастика:
фэнтези
попаданцы
аниме
5.00
рейтинг книги
Мастер темных Арканов

Измена. Испорченная свадьба

Данич Дина
Любовные романы:
современные любовные романы
короткие любовные романы
5.00
рейтинг книги
Измена. Испорченная свадьба

Блуждающие огни

Панченко Андрей Алексеевич
1. Блуждающие огни
Фантастика:
боевая фантастика
космическая фантастика
попаданцы
5.00
рейтинг книги
Блуждающие огни

Пышка и Герцог

Ордина Ирина
Фантастика:
юмористическое фэнтези
историческое фэнтези
фэнтези
5.00
рейтинг книги
Пышка и Герцог

Затерянные земли или Великий Поход

Михайлов Дем Алексеевич
8. Господство клана Неспящих
Фантастика:
фэнтези
рпг
7.89
рейтинг книги
Затерянные земли или Великий Поход

Адвокат Империи 2

Карелин Сергей Витальевич
2. Адвокат империи
Фантастика:
городское фэнтези
попаданцы
аниме
фэнтези
фантастика: прочее
5.00
рейтинг книги
Адвокат Империи 2

Газлайтер. Том 6

Володин Григорий
6. История Телепата
Фантастика:
попаданцы
альтернативная история
аниме
5.00
рейтинг книги
Газлайтер. Том 6

Часовая битва

Щерба Наталья Васильевна
6. Часодеи
Детские:
детская фантастика
9.38
рейтинг книги
Часовая битва

Невеста снежного демона

Ардова Алиса
Зимний бал в академии
Фантастика:
фэнтези
6.80
рейтинг книги
Невеста снежного демона

Законы Рода. Том 3

Flow Ascold
3. Граф Берестьев
Фантастика:
фэнтези
аниме
5.00
рейтинг книги
Законы Рода. Том 3

Случайная свадьба (+ Бонус)

Тоцка Тала
Любовные романы:
современные любовные романы
5.00
рейтинг книги
Случайная свадьба (+ Бонус)

Болотник 2

Панченко Андрей Алексеевич
2. Болотник
Фантастика:
попаданцы
альтернативная история
6.25
рейтинг книги
Болотник 2

Пипец Котенку! 2

Майерс Александр
2. РОС: Пипец Котенку!
Фантастика:
юмористическое фэнтези
попаданцы
аниме
5.00
рейтинг книги
Пипец Котенку! 2