INDY IN DEPTH. ГЛУБИНЫ INDY
17.
Серверы
В Indy есть несколько серверных моделей, в
зависимости от ваших потребностей и используемых протоколов.
Следующие несколько глав введут вас в серверные компоненты Indy.
17.1. Типы серверов
17.1.1. Класс TIdTCPServer
Наиболее известный сервер в Indy – это
TIdTCPServer.
Класс TIdTCPServer создает вторичный
слушающий поток, который независим от главного потока программы.
Слушающий поток следит за входящими запросами от клиентов. Для
каждого клиента, которому он отвечает, он создает новый поток,
для специфического сервиса, индивидуального соединения. Все
соответствующие события затем возбуждаются в контексте этого
потока.
17.1.1.1. Роль потоков
Сервера Indy разработаны вокруг потоков и
работают подобно тому, как в Unix работают сервера. Приложения
Unix обычно взаимодействуют со стеком напрямую с минимумом
абстракции, а то и без нее. Indy изолирует программиста от
стека, используя высокий уровень абстракции и реализует многие
детали внутри, автоматически и скрытно.
Обычно, сервера Unix имеют один или
несколько слушающих процессов, которые следят за запросами от
клиентов. При запросе от клиента, который сервер акцептирует,
сервер размножает процесс, для обработки каждого клиентского
соединения. Обслуживание множества клиентов таким образом очень
просто, так как каждый процесс работает только с одним клиентом.
Процесс также может запускаться в своем контексте безопасности,
который может быть установлен слушателем или процессом на основе
аутентификации, прав или других основаниях.
Сервера Indy работают подобным образом.
Windows в отличии от Unix не размножает процессы, но зато
Windows хорошо работает с потоками. Сервера Indy размещают поток
для каждого клиентского соединения, вместо создания отдельного
законченного процесса, как это делает Unix. Это дает все
преимущества процессов, без наличия недостатков.
17.1.1.2. Сотни потоков
Для сильно нагруженного сервера может
потребоваться сотни или даже тысячи потоков. Есть общее
убеждение, что сотни и тысячи потоков могут убить вашу систему.
Это неверное убеждение.
Количество потоков, которые запущены на
вашей системе, может удивить вас. При минимальном количестве
запущенных серверов и указанными запущенными приложениями:
Моя система имеет 513 созданных потоков:
Вы видите, что даже при 513 потоках
процессор нагружен только на 1%. Сильно нагруженный сервер IIS (Microsoft
Internet Information Server) может создать сотни и тысячи
потоков. Данный тест был выполнен на Pentium III 750 MHz с 256
MB оперативной памяти.
На большинстве серверов, потоки находятся
в режиме ожидания данных. При ожидании на блокирующем сокете,
поток находится в неактивном состоянии. Поэтому на сервере из
500 потоков, только 25 могут оказаться активными одновременно.
В сокетных приложениях, наибольшие
ограничения накладывает самый медленный компонент. В сетевых
приложениях, сетевая плата обычно самый медленный компонент и
поэтому ничего не сможет превысить возможностей сетевой платы.
Даже самая быстрая сетевая плата, во много раз медленнее
процессора, и является узким местом, если она задействована по
полной.
17.1.1.3. Реальные ограничения на потоки
Реально средняя система начинает испытать
проблемы, только когда процесс создаст свыше 1000 потоков, из-за
нехватки памяти. Размер стека потока может быть уменьшен за счет
увеличения количества потоков, тем не менее, надо рассмотреть и
другие альтернативные причины.
Большинству серверов требуется только
несколько сотен потоков. Тем не менее, сильно нагруженные
сервера или сервера, у которых трафик низкий, но множество
подключенных клиентов, таких как чат (Chat) сервер требуют
другой реализации. Такие сервера, в Indy могут создавать тысячи
потоков.
Также следует понять, что количество
клиентов совсем не означает тоже количество конкурирующих
потоков. Когда каждый клиент выделяет отдельный поток, то поток
выделяется клиенту только при соединении. Многие сервера
обслуживают кратко живущие соединения, такие как HTTP сервера.
HTTP соединения для страниц обычно живут только одну секунду или
менее, особенно если используется прокси или кэширование.
Предположим 1 секунду на соединение и только 500 потоков, что
позволяет до 30 000 клиентов в час.
Indy 10 имеет и другие модели, в
дополнение к потокам серверов. Возможности Indy 10 ограничены
только размером доступной памяти для размещения сокетов.
17.1.1.4. Модели серверов
Есть два пути построения TCP серверов: с
командными обработчиками и с событиями OnExecute. Командные
обработчики делают построение серверов много проще, но не во
всех ситуациях.
Командные обработчики удобны для
протоколов, которые обмениваются командами в текстовом формате,
но не очень удобны для протоколов, которые имеют команды
двоичной структуры или совсем не имеют командной структуры.
Большинство протоколов текстовые и могут использоваться
командные обработчики. Командные обработчики полностью
опциональны. Если они не используются сервера Indy продолжают
использовать старые методы. Командные обработчики рассмотрены в
деталях в главе "Командные обработчики".
Некоторые протоколы являются двоичными или
не имеют командной структуры и не пригодны для использования
командных обработчиков. Для таких серверов должно использоваться
событие OnExecute. Событие OnExecute постоянно вызывается пока
существует соединение и передает соединение, как аргумент.
Реализация очень простого сервера с использованием события
OnExecute выглядит так:
Здесь нет необходимости проверять
действительность соединения, так как Indy делает это
автоматически. Так же нет необходимости производить опрос,
поскольку и это Indy делает автоматически за вас. Она вызывает
событие периодически, пока соединение не прекратится. Это может
быть вызвано или явным отсоединением, или по сетевой ошибке, или
если клиент отсоединился. В действительности, не требуется
делать никаких опросов на предмет отсоединений. Если данный
опрос все же необходимо делать, вам надо только позаботиться об
возбуждении исключений, что бы Indy смог нормально их
отработать.
17.1.1.5. Командные обработчики
Indy 9.0 содержит новое средство,
называемое командные обработчики. Командные обработчики – это
новая концепция, используемая в TIdTCPServer, которая позволяет
серверу выполнять парсинг команды и обрабатывать его для вас.
Командные обработчики это разновидность
"
визуального управления
сервером" и является только маленьким заглядыванием в будущие
реализации Indy.
Для каждой команды, которую вы желаете
обрабатывать сервером, создается командный обработчик. Думайте
об командных обработчиках, как о списке действий для сервера.
Командный обработчик содержит свойства, которые указывают, как
парсить параметры, команды, некоторые действия, которые могут
быть выполнены автоматически и дополнительные автоматические
ответы. В некоторых случаях используя только свойства, вы
сможете создать полностью функциональную команду без
необходимости написания какого либо кода. Каждый командный
обработчик имеет уникальное событие OnCommand. Когда событие
вызывается, то нет необходимости определять какая команда была
запрошена, так как данное событие уникально для каждого
командного обработчика. В дополнение, командный обработчик уже
распарсил параметры и выполнил автоматические действия для вас.
Вот маленький пример использования
командных обработчиков. Во-первых вы должны определить две
команды: QUIT и DATE. Создадим два командных обработчика, как
указано ниже:
Для cmdhQuit свойство disconnect
устанавливается в true. Для cmdhDate событие OnCommand
определяется так:
procedure
TForm1.IdTCPServer1cmdhDateCommand(ASender: TIdCommand);
begin
ASender.Reply.Text.Text := DateTimeToStr(Date);
end;
Это законченный код командного
обработчика. Все другие детали, специфицированы установкой
свойств в командных обработчиках.
Командные обработчики более подробно
описаны в главе "командные обработчики".
17.1.2. Класс TIdUDPServer
Поскольку UDP не требуется соединения (по
определению), то класс TIdUDPServer работает отлично от
TIdTCPServer. Подобно TIdSimpleServer класс TIdUDPServer не
имеет некоторых режимов, и поскольку UDP не требуется
соединения, TIdUDPClient имеет только слушающие методы.
При активизации, класс TIdUDPServer
создает слушающий поток, для прослушивания входящих UDP пакетов.
Для каждого, принятого UDP пакета, класс TIdUDPServer возбуждает
событие OnUDPRead в главном потоке или в контексте слушающего
потока, в зависимости от значения свойства ThreadedEvent.
Когда значение свойства ThreadedEvent =
false, то событие OnUDPRead возбуждается в контексте главного
потока программы. Когда значение свойства ThreadedEvent = true,
то событие OnUDPRead возбуждается в контексте слушающего потока.
Когда значение свойства ThreadedEvent
равно false, то блокируется прием дальнейших сообщений. Поэтому
событие OnUDPRead должно быть как можно более быстрым.
17.1.3. Класс TIdSimpleServer
Класс TIdSimpleServer предназначен для
разового использования серверов. Класс TIdSimpleServer
предназначен для обслуживания одного соединения за раз. Хотя он
может обслуживать другие запросы по окончанию, обычно он
используется только для одного запроса.
Класс TIdSimpleServer не создает потоков
для прослушивания или вторичных потоков соединения. Вся
функциональность реализована в одном потоке.
Компонент клиента TIdFTP использует класс
TIdSimpleServer. Когда FTP выполняет передачу, вторичное TCP
соединение открывается для передачи данных и закрывается, когда
данные будут переданы. Данное соединение называется
"
канал
данных (data channel)" и оно уникально для каждого передаваемого
файла.
17.2. События потоков
События TIdTCPServer потоковые. Это
означает, что они не являются частью потока, они выполняются
внутри потока. Это очень важная деталь. Будьте уверены, что вы
понимаете эту деталь до начала обработки.
Это сначала может показаться странным, что
событие может быть частью формы но при этом выполняться внутри
потока. Тем не менее, это было умышленно так сконструировано,
чтобы события созданные в дизайн тайм, были подобны любым другим
событиям, без необходимости создания пользовательского класса и
перекрытия метода.
В создаваемых компонентах наследниках,
перекрытие тоже доступно. Но для построения приложений, модель
обработчиков событий намного проще в применении.
Каждый клиент создает свой собственный
поток. При использовании данного потока, события TCP сервера
(который является частью формы или модуля данных) вызываются из
данных потоков. Это означает, что одиночное событие может быть
вызвано множество раз из разных потоков. Такие события получают
в качестве аргумента AThread, который указывает на поток из
которого возбуждено событие.
Примерами потоковых события являются
командные обработчики сервера OnConnect, OnExecute и
OnDisconnect.
17.3. Модели TCP серверов
TCP сервер Indy поддерживает две модели
для построения серверов. Данные методы это OnExecute и командные
обработчики. Indy 8 поддерживает только OnExecute.
17.3.1 Событие OnExecute
Событие OnExecute ссылается на событие
OnExecute класса TIdTCPServer. При реализации сервера по данной
модели, должно быть определено событие OnExecute или перекрыт
метод DoExecute.
Модель OnExecute допускает полный контроль
разработчиком и позволяет реализовывать любые типы протоколов,
включая двоичные протоколы.
После подсоединения клиента к серверу,
возбуждается событие OnExecute. Если событие OnExecute не
определено, то возбуждается исключение. Событие OnExecute
возбуждается в цикле, как только подсоединяется клиент. Это
очень важная деталь и поэтому разработчик должен побеспокоиться
об:
1. Помнить о том, что событие возникает в цикле.
2. Не препятствовать Indy выполнять обработку в цикле.
Внутренний цикл показан на следующей диаграмме:
На этапе проверки соединения выполняется
следующие проверки:
• Клиент еще подсоединен
• Disconnect не был вызван во время OnExecute
• Отсутствуют фатальные ошибки
• Не было возбуждено необработанное исключение в OnExecute
• Сервер еще активен
Если все эти проверки и другие проверки
истинны, то событие OnExecute возбуждается снова. Поэтому,
разработчик никогда не должен конструировать свой цикл внутри
OnExecute, который будет дублировать это, так как это будет
мешать Indy.
17.3.2. Обработчики команд (Command
Handlers)
Командные обработчики подобны спискам
действий для построения серверов, в стиле визуальной среды
проектирования. Командные обработчики ограничены текстовыми
протоколами. Данные, передаваемые по протоколу, могут быть
двоичными.
Обработчики команд автоматически читают и
разбирают команды. При этом возбуждается специфическое событие
OnCommand.
17.4. Обработчики команд (Command Handlers)
Создание серверов в Indy всегда было
достаточно простой задачей, тем не менее в Indy 9 это стало
проще после введения командных обработчиков в класс TCP сервера
(TIdTCPServer).
Обработчики команд подобны спискам
действий для сервера. Командные обработчики работают следующим
образом: - вы создаете обработчик команд для каждой команды и
затем используя обработчик команд определяете поведение для
конкретной команды. Когда команда принята от клиента, то сервер
автоматически разбирает ее и передает конкретному обработчику.
Обработчики команд не только имеют свойства для настройки
поведения, но также методы и события.
Обработчики команд работают только с
текстовыми командами и ответами TCP протоколов. Тем не менее это
покрывает нужды почти 95% серверов используемых в настоящее
время. Хотя обработчик в состоянии работать с двоичными данным,
но сами команды могут быть только текстовыми. Имеется некоторое
количество протоколов, которые работают с помощью двоичных
команд. Для протоколов, использующих двоичные команды или
текстовые команды, которые не совместимы (от корректора:
труднореализуемые??? Не могу себе представить что он имел в
виду, потому что conversational это словоохотливый,
разговорчивый), реализация обработчиков команд не является
обязательной что позволяет сохранить обратную совместимость.
17.4.1. Реализация
Класс TCPServer содержит свойство,
именуемое CommandHandlers, которое является коллекцией
обработчиков команд. Обработчики обычно создаются во время
разработки, тем не менее, при реализации наследников они могут
создаваться и во время исполнения. Если командные обработчики
создаются во время исполнения, то они должны быть созданы путем
перекрытия метода InitializeCommandHandlers. Это гарантирует,
что они создадутся только во время исполнения. Если они
создаются в конструкторе, они будут создаваться каждый раз,
когда TCPServer загружается из потока и записывается обратно в
поток. Это может привести к созданию множества копий для каждого
обработчика. Инициализация, напротив, вызывается только однажды
после первой активации TCPServer.
Класс TCPServer содержит несколько свойств
и событий имеющих отношение к обработчикам команд. Свойство
CommandHandlersEnabled разрешает или запрещает работу
обработчика как единого целого. Свойство OnAfterCommandHandler
возбуждается после выполнения каждого обработчика и событие
OnBeforeCommand возбуждается перед выполнением каждого
обработчика. Событие OnNoCommandHandler возбуждается если
обработчик, соответствующий команде, не найден.
Если свойство CommandHandlersEnabled равно
true и определены обработчики, то выполняется их обработка.
Иначе вызывается событие OnExecute, если оно назначено.
Обработчик OnExecute не вызывается если были обработаны команды.
Если есть соединение, TCPServer читает
строки текста из соединения и пытается найти подходящий
обработчик команд. Любые пустые строки игнорируются. Для
непустых строк сначала возбуждается событие
OnBeforeCommandHandler. Затем ищется подходящий командный
обработчик. Если командный обработчик найден и его свойство
enabled установлено в true, то возбуждается его событие
OnCommand, а иначе возбуждается событие OnNoCommandHandler.
После всего этого возбуждается событие OnAfterCommand.
17.4.2. Пример протокола
Для демонстрации базовой реализации
обработчиков команд, определим простой протокол. Для
демонстрации пользовательского сервера времени реализуем три
команды:
• Help – показывает список поддерживаемых команд и их форматы.
• DateTime <format> - возвращает текущую дату и время в
указанном формате, если формат не указан, то по умолчанию
используется формат yyyy-mm-dd hh:nn:ss.
• Quit – закрывает сессию и отсоединяется.
Это очень простая базовая реализация, но она работает вполне
приемлемо для целей демонстрации. Конечно, вы можете ее
расширить для того чтобы изучить возможности обработчиков
команд.
17.4.3. Базовый пример
Сначала сконструируем базу для построения
примера. Это подразумевает, что вы уже умеете работать с
TIdTCPServer и вот ваши следующие шаги. Для построения базового
примера выполним следующие шаги:
1. Создадим новое приложение.
2. Добавим TIdTCPServer на форму.
3. Установим свойство TIdTCPServer.Default в 6000. порт 6000 это
порт для демо, выбранный случайным образом, можно использовать
любой свободный порт.
4. Установим свойство TIdTCPServer.Active в True. Это должно
активировать сервер при старте приложения.
Этим мы создали базовое приложение. Оно
пока ничего не делает, поскольку еще нет ни обработчиков команд,
ни событий.
17.4.4. Создание обработчика команд
Обработчики команд создаются при
редактировании свойства CommandHandlers класса TIdTCPServer.
Свойство CommandHandlers – это коллекция. Обработчики могут быть
модифицированы как во время исполнения, так и во время
разработки. Для редактирования обработчиков во время разработки
нажмите на кнопку <…> на свойстве CommandHandlers в инспекторе
объектов. Появится следующий диалог:
Он пока пуст, поскольку еще нет ни одного
обработчика. Для создания обработчика команд, или нажмите правую
кнопку мыши и выберите пункт Add, или нажмите первую кнопку в
панели инструментов диалога. После это в списке появится
обработчик команд.
Для редактирования обработчика выберите
его в инспекторе объектов. Редактирование обработчиков подобно
редактированию полей набора данных БД или колонок в DBGrid. Если
инспектор объектов не виден, то нажмите F11 для его отображения.
Инспектор объектов выглядит как на
приведенном рисунке. Показано, что есть уже одно измененное
свойство, реализующее команду. Это команда QUIT и она будет
обсуждена ниже.
Пошаговое описание реализации команды QUIT:
1. Command = Quit – это команда сервера которую сервер будет
использовать для поиска обработчика при чтении ввода. Команда не
чувствительна к регистру.
2. Disconnect = True – это значит, что
сервер отсоединится от клиента после получении и обработки
данной команды.
3. Name = cmdhQuit – данное свойство не
оказывает никакого влияния на обработку, но оно предназначено
для упрощения идентификации обработчика в коде. Данный шаг
необязательный.
4. ReplyNormal.NumericCode = 200 – Команды
обычно возвращают 3-х разрядный код и необязательный текст.
Задав это свойство, мы указывем обработчику возвращать в ответ
на команду код 200 и дополнительный текст из ReplyNormal.Text
если конечно не произойдет ошибка во время выполнения команды.
5. ReplyNormal.Text = Good Bye –
дополнительный текст, который посылается вместе с
ReplyNormal.NumericCode.
После этого мы имеем полностью
работоспособный обработчик команд.
17.4.5. Поддержка обработчика команд
Теперь когда обработчик создан, есть еще
несколько глобальных параметров, относящихся к северам на
текстовых командах серверов и обработчиков команд, которые также
должны быть установлены. Все это свойства TIdTCPServer, а не
обработчикам команд.
17.4.5.1. Свойство Greeting (приветствие)
Обычной практикой для серверов, является
предоставления информации, приветствия от сервера, перед тем как
сервер начнет обрабатывать команды клиента. Типичный ответ
сервера, показывающий, что сервер готов, это код 200 и установка
кода не равным нулю, разрешит посылку приветствия
Установите свойства Greeting. NumericCode
= 200 и Greeting.Text в "Hello".
17.4.5.2. Свойство ReplyExceptionCode
Если во время обработки команд обнаружатся
не обслуженные исключения, то используется данное свойство со
значением отличным от нуля. Код 500 это типичный ответ для
внутренних, неизвестных ошибок. Вместе с кодом отсылается и
текстовый отзыв.
Установите ReplyExceptionCode в 500.
17.4.5.3. Свойство ReplyUnknownCommand
Если во время обработки команд обнаружатся
не обработанные исключения, то данное свойство будет
использовано для построения ответа, если его значение отличается
от нуля. Код 400 наиболее общий ответ для подобных случаев.
Установите ReplyUnknown.NumericCode в 400
и ReplyUnknown.Text в "Unknown Command".
17.4.5.4. Прочие свойства
У TIdTCPServer есть еще и другие свойства
и события, для реализации дополнительного поведения, относящего
к командным обработчикам, но приведенные выше являются
минимумом, который должен быть реализован.
17.4.6. Тестирование новой команды
Теперь, когда команда уже реализована
можно приступить и к тестированию, просто воспользуемся Telnet,
поскольку протокол текстовый:
1. Запустим приложение.
2. В меню Start: Run введем: telnet 127.0.0.1 6000 и нажмем OK.
Это указывает Telnet подсоединиться к компьютеру на порт 6000,
который используется в демонстрационном примере.
3. Сервер должен ответить 200 Hello, что является приветствием,
из свойства Greeting of TIdTCPServer.
4. Telnet затем покажет каретку. Это означает, что сервер готов
и ожидает команду.
5. Введем HELP и нажмем enter. Сервер ответит "400 Unknown
Command". Поскольку пока мы не создали командного обработчика
для команды HELP и ответ "400 Unknown Command" был взят из
свойства ReplyUnknown.
6. Введем QUIT. Сервер ответит "200 Good Bye" и отсоединится от
клиента.
Поздравляем! Вы построили сервер с обработчиком команд. В
следующей главе мы реализуем остальные две команды - HELP и
DATETIME, которые имеют отличное от команды QUIT поведение.
17.4.7. Реализация HELP
Команда HELP подобно по поведению на
команду QUIT за исключением двух различий.
1. Не происходит разъединение сеанса.
2. В дополнение ответ также предоставляет текстовый отклик со
справочной информацией. Для реализации команды HELP выполним
следующие шаги:
1. Создадим новый командный обработчик.
2. Command = Help
3. Name = cmdhHelp
4. ReplyNormal.NumericCode = 200
5. ReplyNormal.Text = Help Follows
Все эти шаги знакомы вам по реализации
команды QUIT. Дополнительное свойство, которое здесь
используется - это свойство Response, которое является списком
строк. Если свойство Response содержит текст, то оно посылается
клиенту после отсылки ReplyNormal. Для реализации команды HELP
используется редактор строк свойства Response:
Help - Display a list of supported
commands and basic help on each.
DateTime <format> - Return the current date and/or time using
the specified
format.
If no format is specified the format yyyy-mm-dd hh:nn:ss will be
used.
Quit - Terminate the session and disconnect.
Теперь если вы подсоединитесь к серверу и пошлете команду HELP,
то сервер ответит следующим образом:
200 Hello
help
200 Help Follows
Help - Display a list of supported commands and basic help on
each.
DateTime <format> - Return the current date and/or time using
the specified
format.
If no format is specified the format yyyy-mm-dd hh:nn:ss will be
used.
Quit - Terminate the session and disconnect.
17.4.8. Реализация DATETIME
Команда DATETIME – это последняя команда
данного протокола. Оно отличатся и от QUIT и от HELP, в том, что
требует особой функциональности, которая не может быть создана
только с помощью свойств. Для реализации команды DATETIME будет
использован обработчик события.
Для начала построим базовый обработчик
команды, используя шаги с которым вы уже знакомы:
1. Создадим новый командный обработчик.
2. Command = DateTime
3. Name = cmdhDateTime
4. ReplyNormal.NumericCode = 200
В данный момент свойство ReplyNormal.Text
не определяется, обработчик события будет его определять для
каждого ответа. Для определения обработчика, используйте
инспектор объектов, выбрав командный обработчик для DATETIME.
Переключитесь на закладку events и создайте событие OnCommand.
Delphi создаст обработчик следующим образом:
procedure TForm1.IdTCPServer1TIdCommandHandler2Command(ASender:
TIdCommand);
begin
end;
В обработчик OnCommand передается аргумент
of ASender типа TIdCommand. Это не командный обработчик, а сама
команда. Командные обработчики глобальны для всех соединений,
тогда как, команды специфичны для соединения connection и
обрабатываются в рамках экземпляра события OnCommand. Это
гарантирует, что обработчик выполнит корректную обработку для
каждого клиентского соединения.
Перед вызовом обработчика события, Indy
создает экземпляр команды и инициализирует его свойства на
основе данных обработчика команд. Вы можете использовать команды
для смены свойства со значений по умолчанию, вызывать методы для
выполнения задач или для доступа к свойству Connection для
общения с соединением напрямую.
Данный протокол определяет команду
DATETIME, как имеющую дополнительный параметр, указывающий
формат даты и времени. Команда (TIdCommand) реализует это с
помощью свойства Params, которое является списком строк. Когда
команда принимается от клиента и свойство ParseParams
установлено в true (по умолчанию) Indy использует свойство
CmdDelimeter (по умолчанию равное #32 или пробел) для разделения
команды и параметров.
Например, в данном протоколе, клиент может
послать следующее: DATETIME hhnnss
В этом случае, свойство ASender.Params
будет содержать строку "hhnnss" в свойстве ASender.Params[0].
Количество параметров может быть определено с помощью свойства
ASender.Params.Count.
Используя данные свойства обработчик
OnCommand может быть реализован следующим образом:
данная реализация просто читает параметры
и использует ASender.Reply.Text для посылки ответа обратно
клиенту. Не требуется устанавливать ASender.Reply.NumericCode,
так как Indy инициализирует его значением 200, из командного
обработчика ReplyNormal.NumericCode.
Примечание: используйте свойство
ASender.Reply.Text.Text. Указание слова Text дважды требуется
потому что свойство Text команды это список строк и мы имеем
также TStrings.Text в дополнение к этому. Поскольку это список
строк, другие методы или свойства, такие как Add, Delete и
другие также могут использоваться. Свойство Text используется
как ASender.Reply.Text, в некоторых случаях может быть
предварительно проинициализировано и запись в него вызовет
перекрытия текста.
Если снова протестировать пример с помощью
telnet, то теперь ответ будет таким:
200 Hello
datetime
200 2002-08-26 18:48:06
В некоторых случаях свойство Params не
может быть использовано. Свойство DATETIME одно из них.
Представим себе следующую команду:
DATETIME mm dd yy
В данном случае значение свойства
Params.Count будет равно 3 и событие будет неверно обработано,
возвратит только значение месяца (mm). Для данных случаев, когда
значение параметра включает разделители, можно использовать
свойство UnparsedParams. Дополнительно, свойство ParseParams
можно установить в False.
Свойство UnparsedParams содержит данные
независимо от свойства ParseParams, но установка ParseParams в
false увеличивает эффективность, сообщая Indy, что не требуется
разбирать параметры в свойство Params.
Обработчик события, модифицированный для
использования с UnparsedParams:
17.4.9. Заключение
Обработчики команд очень гибки и содержат
большее количество свойств и методов, чем приведено. Это только
введение в обработчики команд и их возможности. Надеюсь, что
этого достаточно, что вызвать у вас интерес и начать работать.
Имеется также особые планы для будущих версий – сделать
командные обработчики еще более наглядными и удобными на стадии
разработки.
17.5. Postal Code Server - реализация
OnExecute
OnExecute Implementation
Demo
Threads
17.6. Postal Code Server – командные
обработчики
Command Handlers
Greeting
CommandHandlers
ReplyException
ReplyTexts
ReplyUnknownCommand
Demo
17.7. Управление потоками
Управление потоками абстрагировано в Indy
в менеджеры потоков. Менеджеры потоков позволяют иметь различные
(даже пользовательские) реализации стратегий управления
потоками.
Управление потоками
– это необязательная
дополнительная возможность. Если вы не определяете менеджер
потоков в свойстве ThreadManager в компоненте, который
поддерживает управление потоками (таком как TIdTCPServer) Indy
неявно создает и уничтожает экземпляр менеджера потоков.
17.7.1. Класс TIdThreadMgrDefault
Стратегия управления потоками по умолчанию
в Indy очень простая. Каждый раз, когда требуется поток, он
создается. Когда поток не нужен, он уничтожается. Для
большинства серверов – это приемлемо и пока вам не понадобится
пул потоков, вы должны использовать стратегию управления
потоками по умолчанию. В большинстве серверов различие в
производительности ничтожное или полностью отсутствует.
Стратегия по умолчанию также дает
дополнительное преимущество, так как, каждый поток гарантировано
"
чистый". Потоки часто распределяют память или другие объекты.
Эти объекты обычно освобождаются автоматически, когда поток
разрушается. Использование управления потоками по умолчанию дает
вам уверенность, что вся память освобождена и все очищено. Когда
используется пуле потоков, то вы должны быть уверены, что все
очищено перед повторным использованием потока. Несоблюдение
этого правила, может привести к тому что, пользователь будет
иметь доступ к информации предыдущего пользователя. Такие
предосторожности не требуются при использовании менеджера
потоков по умолчанию, поскольку все ассоциированные данные
уничтожаются вместе с потоком.
17.7.2. Пул потоков (Thread Pooling)
Обычно диспетчеризация потоков по
умолчанию вполне приемлема. Тем не менее, для серверов, которые
обслуживают коротко живущие соединения, создание и уничтожение
потоков, сравнимо со временем обслуживания запроса. В данной
ситуации, лучше использовать пул потоков.
В пуле потоки создаются предварительно и
используются повторно. Они создаются до использования и хранятся
неактивными в пуле. Когда требуется поток, то он берется из пула
и активируется. Если требуется больше потоков, чем есть в пуле,
то создаются дополнительные потоки. Когда поток больше не
требуется, то вместо его разрушения он деактивируется и
возвращается в пул.
Создание и разрушение потоков может быть
очень интенсивным. Это особо относится к серверам, которые
обслуживают коротко живущие соединения. Такие сервера создают
поток, который используется только короткое время и затем
уничтожается. Это приводит к очень высокой частоте создания и
уничтожения потоков. Примером таких серверов могут служить
сервера времени или даже web сервера. Посылается простой запрос
и отсылается простой ответ.
При использование браузера для просмотра
некоторых сайтов могут создаваться сотни соединений к серверу.
Пул потоков может смягчить данную
ситуацию. Вместо создания и уничтожения потоков по требованию,
потоки выдаются из списка неактивных потоков, которые уже
созданы. Когда поток больше не нужен, он возвращается обратно в
пул. Пока потоки находятся в пуле – они отмечены как
неиспользуемые и поэтому не требуют ресурсов процессора. Как
дальнейшее усовершенствование –
размер пула можно настраивать
динамически, в зависимости от потребностей системы. Indy имеет
поддержку пула потоков. Пул потоков в Indy доступен через
использование компонента TIdThreadMgrPool.
|