Этот пост можно считать как небольшое примечание к книге по Synapse. Потребность в этом примечании возникла по нескольким причинам.
Во-первых, при переходе с Indy на Synapse у разработчиков часто возникают вопросы именно в части того, как Synapse использует различные таймауты, а именно — как установить таймаут на соединение, чтение/запись данных, почему не срабатывает таймаут и т.д. Достаточно забить в поиск фразу «таймаут synapse» и посмотреть различные обсуждения на форумах.
Во-вторых, т.к. библиотека Synapse постоянно развивается и дорабатывается, то может возникнуть ситуация, когда какое-либо свойство объекта Synapse отсутствует в исходниках разработчика, но имеется в той версии библиотеки, которую используем мы, когда пишем о Synapse.
Так как специально вопрос использования тайм-аутов в Synapse мы в главе по сокетам не рассматривали, то, думаю, что этот пост станет небольшим примечанием (или несколькими примечаниями) в книге и поможет избавить разработчиков от некоторых вопросов по использованию Synapse.
Версия Synapse и версии модулей Synapse
На момент написания этой статьи самой актуальной стабильной версией Synapse была Synapse release no. 40. Актуальная версия библиотеки выкладывается на этой странице официального сайта в виде обычного zip-архива, содержащего все модули библиотеки.
Итак, с версией библиотеки разобрались — это двухзначное число (пока двухзначное), которое можно увидеть на странице загрузок
Помимо архива со стабильной версией нам также предоставляется возможность получать самые свежие исходники Synapse из репозитория на SourceForge. О том, как скачивать исходники из SVN можно узнать из двух статей, опубликованных в блоге ранее:
-
Совместная работа над проектом. Работа в проектах на Code.Google. Здесь рассказывается о настройке TortoiseSVN
-
Subversion в Delphi XE. Впечатления после одного дня работы. Здесь вы узнаете как использовать встроенный в Delphi клиент для SVN (сам я встроенным клиентом не пользуюсь, поэтому не могу сказать как сильно изменилась его работа в Delphi XE6 и других версиях Delphi после XE)
Скачивая исходники из ветки trunk репозитория вы будете гарантированно получать самые последние исходники Synapse, но следует помнить, что вместе с самой свежей версией модуля вы можете также получить и самый свежий баг, которого ранее не было.
Скачивая исходники из репозитория вы получаете новые версии модулей Synapse. Версия модуля указывается в исходном коде самой первой строке. Ниже, на рисунке показана версия модуля blcksock.pas:
В итоге такой нумерации модулей и библиотеки в целом может возникнуть такая ситуация, как у меня недавно: версия библиотеки одинаковая — 40, но версии модуля по которому возник вопрос — разные, т.к. разработчик скачал архив с последней версией библиотеки и работает с ней, а я качаю исходники из ветки trunk репозитория. Итог: я даю решение проблемы, которое у меня прекрасно работает, но у другого человека — ошибка, говорящая о том, что свойство класса не определено. И оба при этом правы :) Проблему мы, конечно, успешно решили, но из-за путаницы в версиях модулей, затратили на решение проблемы непозволительно больше времени, чем требовалось.
В итоге я решил, что было бы правильнее в описании того или иного модуля Synapse или при возникновении вопросов по Synapse указывать не только версию библиотеки, но и версию модуля о котором идет речь.
Работа с тайм-аутами в Synapse
В Synapse у сокета (класс TBlockSocket) можно определить следующие тайм-ауты:
1. Тайм-аут для операций записи. Этот тайм-аут устанавливается вызовом метода
procedure SetSendTimeout(Timeout: Integer);
2. Тайм-аут для операций чтения. Устанавливается вызовом метода
procedure SetRecvTimeout(Timeout: Integer);
Если значения обеих этих тайм-аутов вы хотите установить одинаковыми, то достаточно вызвать третий метод:
procedure SetTimeout(Timeout: Integer);
3. Тайм-аут соединения. Этот тайм-аут устанавливается через свойство:
property ConnectionTimeout: Integer read FConnectionTimeout write FConnectionTimeout;
Все значения тайм-аутов устанавливаются в миллисекундах. Например, чтобы установить все три тайм-аута в значение 3 секунды, то достаточно написать:
var Sock: TBlockSocket; begin Sock.SetTimeout(3000); Sock.ConnectionTimeout:=3000; end;
Кроме этого, для работы с тайм-аутом на чтение в Synapse у сокета имеется такое свойство:
property InterPacketTimeout: Boolean read FInterPacketTimeout Write FInterPacketTimeout;
По умолчанию (True) все тайм-ауты определяются как максимальное время ожидания для следующего пакета. Если вы установите это значение в False, то тайм-ауты будут определяться как максимальное время для запрашиваемой операции.
После того, как мы указали значение какого-либо тайм-аута Synapse вызывает исключение в том случае, если время затраченное на операцию будет превышать установленное значение. То есть, если мы указываем Connection Timeout равным 5000 мс, то исключение будет вызвано, если время прошедшее с начала соединения будет равно или больше 5001 мс.
Теперь рассмотрим пример использования тайм-аутов в Synapse.
Проверка на соединение — это, наверное, самый популярный вопрос, касающийся Synapse — как проверить, что соединение прошло успешно и при этом затратить на проверку определенное время?
Например, мы хотим проверить, что какой-либо хост жив и считаем, что если на соединение затрачено больше 5 секунд, то хост «умер».
Для решения этой задачи нам идеально подходит тайм-аут соединения (ConnectionTimeout). Напишем такую функцию:
function Tfmain.CheckHost(const AIP, APort: string; ATimeOut: integer; out AConnected:boolean): single; var ASock: TTCPBlockSocket; iCounterPerSec: TLargeInteger; T1, T2: TLargeInteger; begin ASock:=TTCPBlockSocket.Create; try ASock.ConnectionTimeout:=ATimeOut; //засекаем время QueryPerformanceFrequency(iCounterPerSec); QueryPerformanceCounter(T1); //пробуем соединиться ASock.Connect(AIP,APort); QueryPerformanceCounter(T2); Result:=(T2-T1)/iCounterPerSec;//определяем сколько секунд было затрачено AConnected:=ASock.LastError=0; ASock.CloseSocket;//закрываем сокет. finally ASock.Free; end; end;
Теперь, чтобы проверить соединение и определить за какое время оно было установлено достаточно вызвать эту функцию так:
var TimeElapsed: single; Connected: boolean; begin TimeElapsed:=CheckHost('127.0.0.1','80',5000,Connected); if Connected then ShowMessage('Соединение прошло успешно. Затрачено '+FormatFloat('0.0000', TimeElapsed)+' сек.') else ShowMessage('Ошибка соединения. Затрачено '+FormatFloat('0.0000', TimeElapsed)+' сек.') end;
После того, как соединение произведено, мы можем начинать читать/записывать данные и здесь используются тайм-ауты чтения/записи. Их значения мы можем устанавливать и использовать, например, чтобы закрывать «медленные» соединения, которые не будут отвечать нам заданным критериям. Например, выполнив такой код:
Sock.SetRecvTimeout(1000); Sock.RaiseExcept:=True;
Мы будем получать исключение от Synapse, если очередной пакет данных (или очередная операция чтения) заняла более 1 секунды.
Вот, собственно, все примечания, которые я пока готов внести в книгу по Synapse. В заключение в очередной раз прошу: если у вас есть какие-либо дополнения или исправления по приведенной выше информации, то сообщите мне в комментариях к этой статье или на email: admin @ webdelphi.ru (пробелы убрать). Буду благодарен за ценные замечания, дополнения, новые примеры по теме.
Книжная полка
Описание: Рассмотрены практические вопросы по разработке клиент-серверных приложений в среде Delphi 7 и Delphi 2005 с использованием СУБД MS SQL Server 2000, InterBase и Firebird. Приведена информация о теории построения реляционных баз данных и языке SQL. Освещены вопросы эксплуатации и администрирования СУБД.
|
||
Название: О чем не пишут в книгах по Delphi
Описание: Рассмотрены малоосвещенные вопросы программирования в Delphi. Описаны методы интеграции VCL и API. Показаны внутренние механизмы VCL и приведены примеры вмешательства в эти механизмы. Рассмотрено использование сокетов в Delphi: различные режимы их работы, особенности для протоколов TCP и UDP и др.
|
В скачанной версии release no. 40. версия модуля blcksock.pas 009.008.005, откуда Вы взяли 009.009.001 ???
из репозитория на SourceForge. И об этом в статье написано:
и ещё написано:
и ниже скриншот с модулем версии 009.009.001.
> InterPacketTimeout
Неплохо было бы чуть подробнее пояснить про этот параметр. Сам его ни разу не использовал, но документация указывает на его довольно таки неслабую «хитрость». :)