4.2. Методология Indy

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

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

Работа с Indy аналогична работе с файлами.  Indy позволяет поместить весь код в одно место, вместо создания различных разработчиков событий. В дополнение, многие находят Indy более простым в использовании. Indy также разработан на работу с потоками. Если вы имеет проблемы с реализацией чего-либо в Indy, то вернитесь назад и реализуйте это как для файлов.

5. Блокирующий режим против неблокирующего

5.1. Модели программирования

В Windows есть две модели программирования сокетов – блокирующий и неблокирующий. Иногда они также называются как – синхронный (blocking) и асинхронный (non-blocking). В данном документе мы будем использовать термины блокирующий и неблокирующий. На платформе Unix, поддержан только блокирующий режим.

5.2. Другие модели

В действительности есть еще несколько реализованных моделей. Это completion ports, and overlapped I/O. Но использование этих моделей требует гораздо больше кода и обычно используется только в очень сложных серверных приложениях. В дополнение, данные модели не кросс платформенные и их реализация сильно отличает от одной операционной системы к другой. Indy 10 содержит поддержку и этих моделей.

5.3. Блокирующий режим

В Indy используются вызовы блокирующих сокетов. Блокирующие вызовы очень похожи на чтение/запись файлов. Когда вы читаете файл или пишите файл, то возврат из функции не происходит до ее окончания. Различие состоит в том, что обычно требуется значительно больше времени до окончания. Операции чтения и записи зависят от скорости сети.

С Indy, вы просто вызываете метод Connect и просто ждете возврата из него. Если соединение будет успешное, то будет возврат из метода по окончанию соединения. Если же соединение не произойдет, то будет возбуждено исключение.

5.4. Неблокирующий режим

Работа неблокирующих сокетов основана на системных событиях. После того как произведен вызов, будет возбуждено событие.

Например, для попытки соединения сокета, вы должны вызвать метод Connect. Данный метод немедленно возвращает управление в программу. Когда сокет будет подсоединен, то будет возбуждено событие. Это требует, что бы логика связи была разделена по многим процедурам или использовать циклы опроса.

5.5. История Winsock

В начале был Unix. Это был Berkely Unix. Он имел стандартное API для поддержки сокетов. Это API было адоптировано в большинстве Unix систем.

Затем появился Windows, и кто-то посчитал, что это хорошая идея иметь возможность программировать TCP/IP и в Windows. Поэтому они портировали API Unix сокетов. Это позволило большинство Unix кода с легкостью портировать и в Windows.

5.6. Блокирующий режим это не смертельно

Когда API Unix сокетов было портировано в Windows, то ему дали имя Winsock. Это сокращение от "Windows Sockets".

В Юниксе типично проблема решалась за счет разветвления (похоже на много поточность, но за счет отдельных процессов вместо потоков). Юникс клиенты и демоны (daemons) должны были раздваивать процессы для каждого сокета. Данные процессы затем выполнялись независимо и использовали блокирующие сокеты.

Windows 3.x не мог распараллеливаться и плохо поддерживал многозадачность. Windows 3.1 также не имел поддержки потоков. Использование блокирующих сокетов замораживало пользовательский интерфейс и делало программы не реагирующими. Поскольку это было не приемлемо, то к WinSock были добавлены неблокирующие сокеты, позволяя Windows 3.x с его ограничениями использовать Winsock без замораживания всей системы. Это потребовало другого программирования сокетов, Microsoft и другие страстно поносили блокирующие режимы, что бы скрыть недостатки Windows 3.x.

Затем пришли  Windows NT и Windows 95, Windows стала поддержать вытесняющую многозадачность и потоки. Но к этому моменту мозги уже были запудрены (то есть разработчики считали блокирующие сокеты порождением дьявола), и уже было тяжело изменить содеянное. По этому поношение блокирующих режимов продолжается.

В действительности, блокирующее API единственное которое поддерживает Unix.

Некоторые расширения, для поддержки неблокирующих сокетов были добавлены и в Unix. Эти расширения работают совсем не так как в Windows. Эти расширения не стандартны для всех Unix платформ и не используются широко. Блокирующие сокеты в Unix все еще используются в каждом приложении и будут продолжаться использоваться и дальше.

Блокирующие сокеты также имеют и другие преимущества. Блокирующие сокеты много лучше для поточности, безопасности и по другим аспектам.

5.7. Достоинства блокирующего режима

1.                    Проще программировать - Блокирующие сокеты проще программировать. Весь пользовательский код может находиться в одном месте и выполняться в естественном, последовательном порядке.

2.                    Кросс-платформенность – поскольку Unix использует блокирующие сокеты, по переносимый код легче писать. Indy использует данный факт для использования своего кода между платформами. Другие сокет компоненты, которые кросс платформенные, на самом деле эмулируют это с помощью внутреннего вызова блокирующих сокетов.

3.                    Удобнее работать с потоками - Поскольку у блокирующих сокетов последовательность приобретена по наследственности, поэтому их очень просто использовать в потоках.

4.                    Независимость от сообщенийнеблокирующие сокеты зависят от системы оконных сообщений. Когда используются потоки, то создается отдельная очередь сообщений. Но когда потоки не используются, то узким местом становится обработка множества соединений.

5.8. Недостатки блокирующего режима

1.              Пользовательский интерфейс замораживается в клиентах - Вызов блокирующего сокета не возвращает управления, пока не выполнит свою задачу. Когда подобные вызовы делаются в главном кодовом потоке, то приложение замораживает пользовательский интерфейс. Замораживание происходит, поскольку сообщения обновления, перерисовки и другие сообщения не обрабатываются до окончания вызова блокирующего сокета.

5.9. Компонент TIdAntiFreeze

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

Использование TIdAntiFreeze позволяет получить все преимущества блокирующих сокетов, без видимых недостатков.

5.10. Достоинства неблокирующего режима

1.              Пользовательский интерфейс не замораживается – поскольку пользовательский код обрабатывает оконные сообщения, то имеет контроль и над сокетными сообщениями. Поэтому Windows также может обрабатывать и другие сообщения.

2.              Многозадачность без использования потоков – используется единственный кодовый поток для обработки множества сокетов.

3.              Очень малая нагрузки при множестве сокетов – поскольку множество сокетов могут обрабатываться без потоков, то нагрузка на память и процессор значительно ниже.

5.11. Недостатки неблокирующего режима

1.        Более сложное программированиенеблокирующие сокеты требуют использования опроса или обработки событий. События наиболее используемый метод, а циклы опроса менее эффективны. При использовании обработчиков событий, код размазан по куче процедур, поэтому требуется отслеживание состояния. Это означает большее количество ошибок и более сложная модификация кода.

5.12. Сравнение технологий

Если вы хорошо знаете Indy и его методологию, то вы можете пропустить эту главу. Но даже если вы ранее программировали сокеты, до использования Indy, то все равно данная глава будет вам полезна.

Для тех, кто никогда не программировал сокеты до Indy, то будет легко и естественно использовать его. Но для тех кто программировал сокеты ранее, Indy будет камнем преткновения. Поскольку Indy работает совсем по другому. Попытка программировать в Indy тем же самым образом. Это не означает, что другие решения неправильные, просто Indy работает иначе. Пытаться программировать в Indy так же, как с другими сокетными библиотеками, равносильна попытке приготовить пирожное в микроволновой печи, как в духовке. Результатом будет испорченное пирожное.

Чтобы подчеркнуть разницу, приведем абстрактный пример. Для абстрагирования концепции, используем в качестве аналога файлы.

5.13. Файлы против сокетов

Разница между файлами и сокетами в основном в скорости доступа. Доступ к файлу не всегда быстрый. Флоппи диски, сетевые диска, ленточные устройства архивирования и иерархические системы хранения часто имеют медленную скорость.

5.14. Сценарий записи в файл

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

1.             Открыть файл

2.             Записать данные

3.             Закрыть файл

5.15. Блокирующий режим записи файла

Блокирующая запись в файл выглядит следующим образом:

procedure TForm1.Button1Click(Sender: TObject);

var

  s: string;

begin

  s := 'Indy Rules the (Kudzu) World !' + #13#10;

  try

    // Open the file

    with TFileStream.Create('c:\temp\test.dat', fmCreate) do

    try

      // Write data to the file

      WriteBuffer(s[1], Length(s));

      // Close the file

    finally

      Free;

    end;

  end;

end;

Как вы видите, это практически повторяет приведенный выше псевдокод. Код последовательный  и легкий для понимания.

5.16. Неблокирующий режим записи файла

Не существует такой вещи как неблокирующий режим записи файла (может быть исключая overlapped I/O, но это за пределами данной книги), но здесь мы можем просто эмулировать механизм это. File1 это условный неблокирующий компонент, размещенной на форме.

procedure TForm1.Button1Click(Sender: TObject);

begin

  File1.Filename := 'd:\temp\test.dat';

  File1.Open;

end;

procedure TForm1.File1OnOpen(Sender: TObject);

var

  i: integer;

begin

  FWriteData := 'Hello World!' + #13#10;

  i := File1.Write(FWriteData);

  Delete(FWriteData, 1, i);

end;

procedure TForm1.File1OnWrite(Sender: TObject);

var

  i: integer;

begin

  i := File1.Write(FWriteData);

  Delete(FWriteData, 1, i);

  if Length(FWriteData) = 0 then

  begin

    File1.Close;

  end;

end;

procedure TForm1.File1OnClose(Sender: TObject);

begin

  Button1.Enabled := True;

end;

Потратим немного времени, что бы попытаться понять, что здесь делается. Если вы используете неблокирующие сокеты, то вы должны легко понимать данный код. Это примерно следующее:

1.             При вызове Button1Click открывается файл. Метод Open немедленно вернет управление в программу, но файл еще не открыт и нельзя с ним еще нельзя работать.

2.             Обработчик события OnOpen будет возбужден, когда файл будет открыть и готов к работе. Делается попытка записать данные в файл, но все данные еще не акцептированы. Метод Write вернет количество записанных байт. Оставшиеся данные будут сохранены позже.

3.             Обработчик события OnWrite будет возбужден, когда файл будет готов воспринять следующую порцию данных, и метод Write будет повторяться для оставшихся данных.

4.             Шаг 3 повторяется до тех пор, пока все данные не будут записаны методом Write. По окончанию записи всех данных вызывается метод Close. Но файл пока еще не закрыт.

5.             The OnClose event is fired. The file is now closed.

5.17. Сравнение записи файлов

Оба примера только записывают данные. Чтение и запись данных будут сложнее для неблокирующего режима, но только добавлением одной строки для блокирующего режима.

Для блокирующего примера, просто откройте, записывайте данные, и закройте файл когда необходимо:

·              3 File1 события

·              1 поле в Form

Неблокирующая версия более сложная и более тяжелая для понимания. Дадим шанс выбора между обеими, если надо будет выбирать, то большинство выберет неблокирующий путь. Большинство C++ программистов, исключая конечно просто мазохистов или вообще не будет выбирать, поскольку все они почти просты. Почти все сокетные функции используют неблокирующий режим.

5.18. Почти как файлы

Использование Indy почти равносильно использованию файлов. В действительности Indy еще проще, поскольку Indy имеет ряд методов для чтения и записи. Indy пример, эквивалентный примеру с файлами выглядит так:

with IndyClient do

begin

     Connect;

     Try

       WriteLn('Hello World.');

     finally

       Disconnect;

     end;

end;

Как вы можете видеть, Indy в действительности очень похож работе с файлами. Метод Connect замещает функцию Open, а метод Disconnect замещает функцию Close. Если вы думаете и признаете сокеты как чтение и запись в файл, то вам будет использовать Indy очень просто.

 

Hosted by uCoz