Налаштування обладнання та програмного забезпечення

Розробка клієнт-серверних програм з використанням Indy в Delphi.

Серж Досюков (Serge Dosyukov) Майк Фем (Mike Pham)

У статті розповідається про те, як створити автономну Web-службу, використовуючи комплект Indy та Delphi 7 та як використовувати комплект Indy для здійснення підтримки у Delphi 7 Web-служб на основі протоколу SOAP. За додатковою інформацією про створення Web-служб рекомендуємо звернутися до чудової статті Ніка Ходжеса (Nick Hodges) на сайті спільноти Borland: "Шекспір ​​у мережі".

Рано чи пізно може виникнути необхідність створення сервера, який був автономним HTTP-сервером і здійснював підтримку Web-служб. Наприклад, може знадобитися створити сервер додатків на основі протоколу SOAP для n-рівневої програми, створеної за допомогою Delphi.

Вступ

Інтерактивна довідка Delphi надає чудову послідовну інструкцію про те, як створити Web-службу, MIDAS-сервер (COM, DCOM-модель), але в ній практично немає інформації про створення автономного n-рівневого MIDAS-додатку на основі протоколу SOAP.

Раніше було опубліковано Дейва Нотажа (Dave Nottage). У цій статті було описано ідею про те, як створити в Delphi 6 Web-службу з підтримкою SOAP та можливістю публікації SOAP-інтерфейсів модуля Datamodule, тобто ця стаття дозволяла навчитися створювати власні n-рівневі MIDAS-системи.

Випущені компанією Borland Delphi 7 і новий комплект Indy мають вбудовану підтримку такої функціональності.

Однак, незважаючи на вбудовану підтримку, документального опису цієї можливості немає.

Нещодавні повідомлення в мережевій конференції Borland і пошук у мережі за допомогою сервера Google дозволили авторам розробити спосіб перетворення існуючого коду з Delphi 6 на Delphi 7. Але - всьому свій час.

Основна ідея

Ця стаття є першою частиною циклу із трьох статей. У ньому описуються основні тези. Друга та третя частини будуть присвячені деяким проблемам та способам їх вирішення. Приступимо до опису основної ідеї.

  • бути автономним HTTP-сервером;
  • використовувати Indy як платформу;
  • підтримувати публікацію за протоколом SOAP;
  • бути спроможним до публікації SOAP-модулів DataModules, що дозволило б створити власний n-рівневий сервер на основі SOAP/HTML.

HTTP-сервер та SOAP

Багато хто знає Indy і використовував компоненти THTTPServer раніше. Чи не складно помістити цей компонент на форму програми, але як змусити його підтримувати SOAP? У каталозі "C:Program FilesBorlandDelphi7SourceIndy" можна знайти файл IdHTTPWebBrokerBridge.pas. Це саме те, що потрібно.

Цей файл не є частиною модуля Indy, тому потрібно включити його в поточний проект як стандартний проектний файл. (Для компіляції проекту також знадобиться файл IdCompilerDefines.inc.) Ці файли необхідно скопіювати в каталог поточного проекту. Для збільшення швидкості може знадобитися зміни коду, тому ці файли краще зберігати окремо від дистрибутива Indy.

Далі описується реалізація заміщення компонента з THTTPServer, розширеного підтримки пакетів SOAP і називається TIdHTTPWebBrokerBridge. Ця конструкція є класом, що успадковується від TCustomHTTPServer і підтримує базову прив'язку запитів.

Так як цей клас недоступний з палітри, необхідно визначити його як регулярний об'єкт при виконанні програмного коду.

Цей об'єкт можна використовувати так само, як і звичайний THTTPServer, за винятком тих додаткових властивостей, які забезпечують роботу з SOAP.
Однак спочатку розглянемо підготовку необхідного коду.

WebBroker та Indy

Тим, кому раніше доводилося створювати Web-служби, відомо, що для цього використовується WebBroker. Delphi 7, як і Delphi 6, використовує архітектуру WebBroker підтримки SOAP.

Тому потрібно створити модуль TWebModuleта помістити в нього наступні три компоненти: THTTPSoapDispatcher, THTTPSoapPascalInvoker та TWSDLHTMLPublish. Усі вони доступні з вкладки WebServices на панелі компонентів. Після зв'язування SOAPDispatcher з SOAPPascalInvoker форма програми готова. Як кінцевий результат має вийде щось подібне до того, що зображено на наступному малюнку:

(модуль uWebModule.pas)

Краще все залишити як є, тому що немає необхідності змінювати або виконувати будь-який власний код для цієї форми.

WebModule та Indy

Перейдемо до іншої частини коду, яка потрібна для реалізації HTTP-сервера.

Як можна помітити TIdHTTPWebBrokerBridge має метод RegisterWebModuleClass, який дозволяє зареєструвати власний модуль WebModule і зробити його доступним для сервера.

Таким чином, після створення серверного об'єкта fServer потрібно просто викликати клас fServer.RegisterWebModuleClass (TwmSOAPIndy).

Примітка.При звичайній реалізації TIdHTTPWebBrokerBridge об'єкт TwmSOAPIndy буде створюватися щоразу, коли надходить запит. Очевидно, що в цьому немає потреби. Тому клас можна модифікувати так, щоб забезпечити перманентне створення даного об'єкта протягом часу, поки існує об'єкт Server. За додатковою інформацією рекомендується звернутись до документації щодо реалізації класів.

Чи готовий сервер?

Досить хороший протокол UDP передачі текстових повідомлень, тобто можна організовувати локальні чати тощо. Я вирішив навести приклад найпростішої роботи з UDP на Delphi.

Покрокова інструкція:

Приклад навести-то навів, але Ви вже вибачте, не став розписувати кожен рядок, т.к. я не бачу нічого складно, і кожен охочий може в цьому розібратися.

Власне, якщо щось незрозуміло можна поставити мені запитання. А ось власне і код:

uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls, IdUDPSServer, IdBaseComponent, IdComponent, IdUDPBase,
IdUDPClient, IdSocketHandle;

type
TForm1 = class(TForm)
IdUDPClient1: TIdUDPClient;
IdUDPServer1: TIdUDPServer;
Button1: TButton;
Label1: TLabel;
procedure FormCreate(Sender: TObject);
procedure FormClose(Sender: TObject; var Action: TCloseAction);
procedure Button1Click(Sender: TObject);
procedure IdUDPServer1UDPRead(AThread: TIdUDPListenerThread; AData: TBytes;
ABinding: TIdSocketHandle);
private
(Private declarations)
public
(Public declarations)
end;

var
Form1: TForm1;

($R *.dfm)
[b]//Процедура надсилання повідомлення
procedure TForm1.Button1Click(Sender: TObject);
begin
try
IdUDPClient1.Active:= True;
IdUDPClient1.Host:= "localhost";
IdUDPClient1.Connect;
if IdUDPClient1.Connected then
begin
IdUDPClient1.Send(TimeToStr(Time));
Label1.Caption: = "ok";
end;
IdUDPClient1.Active:= False;
Beep; Beep; Beep;
except
MessageDlg("Щось не вийшло =(", mtError, , 0);
end;
end;
[b]
//ВКЛ викл. сервера UDP під час запуску та закриття форми
procedure TForm1.FormClose(Sender: TObject; var Action: TCloseAction);
begin
IdUDPServer1.Active:= False;
end;

procedure TForm1.FormCreate(Sender: TObject);
begin
IdUDPServer1.Active:= True;
end;

[b]//Процедура реакції сервера при отриманні даних
procedure TForm1.IdUDPServer1UDPRead(AThread: TIdUDPListenerThread;
AData: TBytes; ABinding: TIdSocketHandle);
Var
i:Integer;
s:String;
begin
s:="";
try
i:= 0;
while (AData[i] 0) do
begin
s: = s + chr (AData [i]);
i:= i + 1;
end;
finally
Label1.Caption: = s;
end;
end;

Окрім базових служб і протоколів Інтернету існує широкий набір додаткових сервісів, можливості яких часто використовуються Інтернет-розробниками. До того ж, далеко не завжди можливість відображення інформації за допомогою браузера є прийнятним рішенням для Інтернет-додатків. У цьому випадку розумно використовувати Інтернет-інфраструктуру для обміну даними, а відображення інформації забезпечити за рахунок складніших клієнтських програм, розроблених, припустимо, на Delphi.

Допустимо, потрібно реалізувати спеціалізовану серверну логіку, яка не закладена у стандартні Web-сервери. Для вирішення такого класу завдань до складу Delphi включено бібліотеку Internet Direct (Indy) компанії Nevrona Designs (http://www.nevrona.com/Indy/). Ця бібліотека, розроблена спеціально для Borland Delphi, налічує вже вісім версій, остання з яких увійшла до складу нової версії Delphi. Набір компонентів поділено на три групи: клієнтські (Indy Client), серверні (Indy Servers) та допоміжні (Indy Misc).

Indy Clients та Indy Servers

Більшість компонентів Indy Client та Indy Servers є парою, що відповідає клієнтським і серверним частинампротоколів і служб (за винятком окремих, в основному серверних, компонентів типу TunnelMaster і TunnelSlave), і дозволяють використовувати такі протоколи, як TCP/IP, UDP, NNTP, SMTP, FTP, HTTP, а також служби ECHO, FINGER, WHOIS тощо .д. (Рис. 1).

Клієнтські компоненти Indy написані із використанням сокетів. Сокет клієнта вимагає з'єднання з сервером. Якщо зв'язок встановлено, клієнт та сервер можуть розпочинати обмін повідомленнями. Ці повідомлення мають різний характер, але зазвичай обмін відбувається за певним протоколом (наприклад, НТТР) (рис. 2).

TIdTCPClient та TIdTCPServer

Ці компоненти використовуються підтримки одного з основних мережевих протоколів - ТСР (Transmission Control Protocol), і навіть є базовими класами для компонентів TIdSMTP і TIdFTP. Клас TIdTCPServer має властивість ThreadMgr, за умовчанням рівним nil. Якщо ThreadMgr дорівнює nil, коли TIdTCPServer активізовано, клас TIdThreadMgrDeafault буде створено неявно. У інакшевикористовується встановлений менеджер процесів.

TIdUDPClient та TIdUDPServer

Ці компоненти використовуються для підтримки мережевого протоколу UDP (User Datagram Protocol), а також є базовими класами для інших компонентів Indy.

TIdDICTServer

Серверний компонент, що підтримує протокол Dictionary Server Protocol (DICT) – серверний словник на базі TCP-протоколу, який дозволяє клієнту отримувати доступ до словника природної мови.

TIdDISCARDServer

Серверний компонент підтримує сервер записів. Записи можуть бути використані як інструмент налагодження та проведення вимірювань. Служба записів просто передає будь-які дані тому, хто готовий їх приймати.

TI dEcho та TI dECHOServer

Компоненти призначені для забезпечення служби відгуку, яка зазвичай використовується для перевірки працездатності мережі. Клієнт посилає текстове повідомленнясервер, сервер повертає повідомлення клієнту. Якщо повідомлення спотворене, мережа працює з помилками.

TIdFinger та TIdFingerServer

Компоненти призначені для забезпечення протоколу, що дозволяє користувачеві запитувати дані щодо присутності в системі інших користувачів. Деякі сервери обробляють такі запити клієнта. Використання цієї пари компонентів дозволить здійснити обслуговування клієнтських запитів, які з'ясовують наявність у системі інших користувачів.

TIdFTP

Компонент включає повну підтримкупротоколу передачі файлів – FTP (File Transfer Protocol). Підтримується пасивна та активна передача даних, а також такі операції, як GET та PUT, видалення директорій, отримання квот, розмірів файлів та каталогів. У роботі TI dFTP використовує клас TIdSimpleServer. При передачі файлу за протоколом FTP вторинне з'єднання протоколу TCP відкрито для передачі даних і закривається, коли дані були передані. Таке з'єднання називається «канал передачі даних», унікальний для кожного файлу, що передається.

TIdGopher та TIdGopherServer

Ці компоненти призначені для забезпечення мережевого протоколу, витісненого останнім часом із WWW (World Wide Web) протоколом HTTP. Сервер, який реалізує цей протокол, забезпечує ієрархічну розподілену систему підтримки документообігу. Приклад використання цієї пари компонентів, що знаходиться в директорії \demos\indy\GopherClient і \demos\indy \GopherServer, демонструє, як за допомогою цього протоколу можна надавати в локальної мережіінформацію про файли, що знаходяться на вашому комп'ютері, у тому числі про закриті.

TIdHostNameServer

Серверний компонент, призначений передачі клієнтам локального імені сервера.

TIdHTTP та TIdHTTPServer

Компоненти використовуються для забезпечення мережевого протоколу HTTP (підтримуються версії 1.0 та 1.1, включаючи операції GET, POST та HEAD). Крім того, забезпечується підтримка аутентифікації та застосування proxy-серверів. Серверний компонент використовується для надання послуг іншому Web-серверу, який підтримує цей протокол. TIdHTTPServer полегшує реалізацію таких функцій, як cookies, керування станами та ін.

TIdIcmpClient

Клієнтський компонент, призначений для забезпечення протоколу ICMP (Internet Control Message Protocol), за допомогою якого здійснюється виконання операції ping та трасування мережі.

TIdPOP3

Клієнтський компонент, призначений для забезпечення POP (Post Office Protocol), включаючи підтримку MIME-кодування та декодування, а також передачу багатобайтних символів.

TIdIMAP4Server

Серверний компонент, призначений для підтримки операцій з протоколом IMAP (Internet Message Access Protocol) на сервері. Протокол дозволяє здійснювати пошук повідомлень електронної поштина сервері. Різниця протоколів IMAP і РОР полягає в тому, що протоколу РОР потрібна додаткова пам'ять для зберігання даних, а протокол IMAP звертається до сервера замість клієнтської машини. IMAP4 створювався для заміни POP3, проте досі протокол POP3 залишається стандартом, що широко використовується.

TIdIRCServer

Серверний компонент, призначений для підтримки сервісних операцій, що найчастіше використовуються в Інтернеті, зазвичай званих chat (для дружніх бесід). Компонент забезпечує базові конструктивні блоки для сервера IRC (Internet Relay Chat).

TIdMappedPortTCP

Серверний компонент, призначений для створення портів, які часто використовуються в proxy-серверах. Методи цього компонента дають змогу відобразити один порт на інший. Наприклад, порт 80 може бути відображений до порту 3000 і всі запити до першого порту (порт 80) будуть переадресовані на другий порт (порт 3000).

TIdNNTP та TIdNNTPServer

Ці компоненти необхідні для забезпечення мережного протоколу NNTP (Network News Transfer Protocol), який використовується у службах новин. Клієнтський компонент включає підтримку MIME-кодування та декодування, а також підтримку багатобайтних символів та альтернативних кодувань. Серверний компонент дозволяє створювати сервери новин. Важливо, що TIdNNTPServer є повнофункціональним сервером новин, а компонентом, що забезпечує базові можливості такого сервера.

TIdQOTD та TIdQOTDServer

Компоненти використовують для забезпечення служби «цитат дня» (Quote of the Day). За допомогою компонента клієнта здійснюється з'єднання з екземпляром серверного компонента для отримання щоденної цитати. Кожен екземпляр сервера містить унікальну базу даних цитат.

TIdSMTP

Клієнтський компонент, призначений для застосування у додатках протоколу SMTP (Simple Mail Transfer Protocol), забезпечення підтримки автентифікації, MIME-кодування та декодування, а також підтримки багатобайтних символів.

TIdSNTP

Клієнтський компонент призначений для забезпечення протоколу SNTP (Simple Network Time Protocol) - служби часу. Може використовуватися для з'єднання з будь-якою службою часу з метою визначення поточної дати та часу.

TIdSimpleServer

Серверний компонент забезпечує полегшений ТСР-сервер. Дозволяє організовувати з'єднання «крапка-крапка». Використовується для створення серверів з єдиним користувачем, тобто може одночасно обслуговувати лише одне підключення. На відміну від компоненту TIdTCPServer не породжує вторинні процеси при очікуванні запитів від клієнтів та при обробці цих запитів. Іншими словами, якщо сервер обслуговує запит від якогось клієнта, а в цей час до нього звертається для підключення інший клієнт, він буде блокований до кінця обробки першого запиту.

TIdTelnet та TIdTelnetServer

Клієнтський компонент використовується для організації віддалених сеансів на іншому комп'ютері, включаючи консольні переговори та автентифікацію. Протокол зв'язку передбачає наявність людини, яка здійснює інтерактивну взаємодію із сервером. Клієнтський компонент не має підтримки дисплея та емуляції терміналу, а просто забезпечує з'єднання із серверною частиною. Зазвичай серверний протокол TIdTelnetServer використовується для організації віддалених базданих із текстовим інтерфейсом для інтерактивної взаємодії з клієнтами.

TIdTime та TIdTimeServer

Клієнтський компонент є альтернативою компоненту TIdSNTP визначення часу. Важливо, що формати цих двох протоколів різні. TIdTime заснований на форматі RFC 868 (повертає час у внутрішньому стандарті ОС UNIX, виконуючи всі необхідні перетворення). Серверний компонент подібний до функціонування DayTime-серверу. Може використовуватися для служби часу на локальному комп'ютері. Додаткового коду не потрібно, достатньо створити екземпляр TIdTimeServer, який повертатиме час внутрішнього годинника серверного комп'ютера.

TIdTrivialFTP та TIdTrivialFTPServer

Ці компоненти необхідні організації найпростішого протоколу передачі файлів. Клієнтський компонент цього протоколу використовується для з'єднання з екземпляром відповідного серверного компонента. Протокол призначений для окремих, полегшених, локальних випадків передачі файлів, наприклад у локальних обчислювальних мережах або для завантаження (вивантаження) таблиць маршрутизації в маршрутизатори. Зважаючи на ослаблені характеристики цього протоколу, його використання не рекомендується у разі застосування алгоритмів автентифікації або будь-яких інших механізмів захисту. Основне призначення даного протоколу - передача файлів апаратного пристрою з його модифікації.

TIdTunnelMaster і TIdTunnelSlave

Серверні тунельні компоненти використовуються в proxy-серверах для організації множинних логічних сполук поверх одного фізичного (тунелю). Ці класи можна застосовувати для різних цілей, наприклад, для організації секретного з'єднання несекретними каналами.

TIdWhois та TIdWhoIsServer

Цей клієнтський компонент здійснює з'єднання з будь-яким стандартним Whois-сервером, що дозволяє отримати інформацію про домени. Серверний компонент забезпечує базову функціональність сервера NIC.

Indy Misc

Сторінка палітри компонентів Indy Misc (Indy Miscellaneous Components) включає кодеки BASE64, UUE, Quoted Printable та інші поширені формати обміну даними через e-mail, кодери (MD2, MD4 та MD5) для стандартів криптографії, що використовуються для зберігання паролів та електронних підписів (Важко піддається дешифрування) вигляді, а також багато інших корисних компонентів і утиліти, що часто застосовуються при розробці Інтернет-додатків (рис. 3).

TIdAntiFreeze

Внаслідок блокової організації алгоритмів компонентів Indy найчастіше складається враження, що програма «зависла», тоді як з'єднання працює. Щоб виключити використання вторинних процесів (threads) при організації комунікацій для запобігання заморожування (freeze) програми, достатньо помістити на форму зазначений компонент.

Компонент працює, аналізуючи запити з стека протоколу TCP/IP і посилаючи повідомлення додатку під час затримки у разі блокування зовнішніх з'єднань, що створює ілюзію працюючого коду. Оскільки вплив компонента здійснюється на блоковані з'єднання тільки для головного процесу, використання TIdAntiFreeze у вторинних процесах програми не потрібне. Необхідно пам'ятати, що компонент TIdAntiFreeze уповільнює роботу з'єднань, оскільки робота головного процесу періодично переривається для обробки повідомлень. Звідси випливає, що треба дбати про те, щоб додаток, що розробляється, не витрачав занадто багато часу на обробку повідомлень, включаючи OnClick, OnPaint, OnResize та ін. В якійсь мірі цим можна керувати через властивості класу TIdAntiFreeze. Використання цього компонента не є обов'язковим, але дозволяє вирішити проблему синхронізації з'єднань із візуальним інтерфейсом програми.

TIdDateTimeStamp

Клас для виконання математичних дій з датою та часом, пов'язаних з тим, що Інтернет-протоколи використовують різні формати дати та часу; крім того, клієнти та сервери можуть перебувати в різних часових поясах.

TIdLogDebug

Призначення даного компонента – перехоплювати події будь-якого клієнтського чи серверного компонента та поміщати запис про подію у вказаний файл. Цей компонент дуже корисний для налагодження компонентів Indy.

TIdMessage

Компонент використовується в комбінації з іншими компонентами, щоб розшифрувати або кодувати повідомлення належним чином. Це можуть бути POP-, SMTP- та NNTP-компоненти. Клас підтримує MIME-шифрування та розшифрування, багатобайтні символи та кодування ISO.

TIdNetworkCalculator

Один із небагатьох компонентів Indy, який можна використовувати при конструюванні програм. Мережевий калькулятор може служити для обчислень над IP-адресами, включаючи мережні маски, підмережа, класи мережі і т.д.

TIdThreadMgrDefault

Компонент забезпечує керування вторинними процесами за умовчанням. Створюється у випадку, якщо для будь-якого компонента Indy, що підтримує керування процесами, не визначено екземпляр класу TIdThreadManager. Компонент забезпечує лише основні можливості управління вторинними процесами: створює та знищує їх на вимогу.

TIdThreadMgrPool

Більш просунутий компонент управління процесами, ніж TIdThreadMgrDefault, тому що він об'єднує процеси, а не створює або знищує їх на вимогу.

TIdVCard

VСard - електронний еквівалент візитної картки, що може містити персональну інформацію власника, графічні дані.

TIdIMFDecoder

Призначений для декодування Інтернет-повідомлень. Є спадкоємцем класу TIdCoder, як і й інші компоненти-кодировщики. Клас TIdCoder здійснює декодування відповідно до стандарту формату текстових Інтернет-повідомлень ARPA RFS-822, запропонованого в серпні 1982 року, та стандарту для обміну USENET-повідомлень RFC 1036, запропонованого у грудні 1987 року.

Компонент розширює можливості класу TIdCoder, дозволяючи виявляти формат RFS-822 за контекстом заголовків, забезпечуючи режим розшифровки при прийомі та MIME-шифрування та розшифровку. Компонент TIdIMFDecoder використовується в класі TIdMessageClient для декодування повідомлень, що отримуються і передаються.

TIdQuotedPrintableEncoder

QuotedPrintableEncoder дозволяє розшифровувати текст у зазначеному форматі. Може служити як автономний компонент із зазначеним типом кодування, що дозволяє передавати повідомлення, що містять кодування нового типу.

TIdBase64Encoder

Реалізує ще один алгоритм шифрування, який дає можливість передавати символи, що не друкуються.

TIdUUEncoder

Реалізує один із перших шифроалгоритмів, UU-кодування. Іноді використовується при поштових пересиланнях статей у службі новин.

TIdXXEncoder

Цей метод шифрування навряд чи буде використовуватися. По суті, це те саме UU-кодування, але з іншою таблицею шифрування.

TIdCoderMD2

Компоненти з різними різновидами алгоритму шифрування MD (Message Digest). Усі вони засновані на перемішуванні, є односторонніми і не мають алгоритмів розшифровування.

Компоненти протокольних клієнтів та серверів можуть бути використані для розробки серверних та клієнтських Інтернет-додатків, спільно або замість базових (ClientSocket, ServerSocket) та інших компонентів зі складу панелі Internet та Fastnet. Компоненти Indy не використовують архітектуру WebBroker, реалізуючи підтримку Інтернет-протоколів та служб на нижньому рівні безпосередньо у своєму вихідний код(Вихідні коди додаються).

TIdConnectionInterceptOpenSSL та TIdServerInterceptOpenSSL

Протокол SSL - Secure Sockets Layer (Секретний рівень Сокетів), що забезпечує секретність і надійність зв'язку між двома додатками, має два рівні. На низькому рівні багаторівневого транспортного протоколу (наприклад TCP) SSL є протоколом запису і використовується для інкапсуляції різних протоколів вищого рівня. Перевага SSL полягає в тому, що він є незалежним протоколом прикладної програми, при цьому протокол вищого рівня може бути використаний поверх SSL.

SSL здійснює захист зв'язку, який має три основні функції: забезпечення конфіденційного з'єднання; шифрування з відкритим ключем (використовується для підтвердження автентичності адресата); підтримка надійності передачі.

  • Симетрична криптографія використовується для шифрування даних (наприклад, DES, RC4 тощо).
  • Цифровий підписзабезпечується за допомогою асиметричного шифрування з відкритим ключем (наприклад, RSA, DSS тощо).
  • Надійність зв'язку, транспортування повідомлення включає перевірку цілісності повідомлення за допомогою коригувальних кодів MAC, безпечних хеш-функцій (наприклад, SHA, MD5 тощо) з використанням MAC-обчислень.

У поєднанні з протоколом HTTP та автентифікацією сервера протокол SSL забезпечує необхідні функції шифрування і надалі підтримує встановлене з'єднання, перевіряючи ще раз автентичність Web-сервера і т.п. Важливо зрозуміти, що SSL лише захищає зв'язок у процесі передачі, а не замінює інші захисні механізми.

Компоненти TIdConnectionInterceptOpenSSL та TIdServerInterceptOpenSSL забезпечують з'єднання як з боку клієнта, так і з боку сервера відповідно до протоколу SSL. Необхідно зазначити, що компоненти TIdConnectionInterceptOpenSSL і TIdServerInterceptOpenSSL є лише у Delphi 6, а Kylix відсутні. Це пов'язано зі складністю протоколу, який у разі реалізації Windows ґрунтується на функціях операційної системи.

Приклади використання компонентів Indy можна знайти у каталогах /Delphi6/Demos/Indy. Усього бібліотека Indy у версії 8.0 містить 69 компонентів. Заявлено, що у версії 9.0 зазначена бібліотека міститиме 86 компонентів. Всі компоненти уніфіковані і включені як Delphi 6, так і Kylix, що дозволяє використовувати їх для розробки крос-платформних додатків. Усі компоненти Indy підтримують багатопоточність.

У компонентах Indy реалізована майже вся функціональність, що є в компонентах Internet і Fastnet, що показано в таблиці .

Виняток становлять такі класи, як TNMMsgServ, TNMMsg, TNMStrm, TNMStrmServ, TpowerSock, TNMGeneralServer, TNMURL, які або реалізують морально застарілі протоколи, або мають функціональність, реалізовану у великій групі альтернативних класів.

Однак на відміну від своїх попередників - компонентів Internet і Fastnet, Indy багатше представлені серверні компоненти і компоненти перекодування і шифрування даних, а також підтримка аутентифікації (палітра Indy Misc). Як видно з наведеної вище таблиці, основні протоколи та служби забезпечуються як клієнтськими, а й серверними компонентами. Це служби часу, відгуку, отримання інформації про користувача, а також протоколи HTTP, NNTP, UDP і навіть найпростіший варіант FTP.

Деякі приклади застосування компонентів Indy

Компоненти Indy, які містяться в Delphi, IP-адреса визначається у властивості Host, як правило, тільки в клієнтських додатках. Компоненти, що розміщуються на сервері, мають методи, що дозволяють почати або припинити опитування відповідного порту, наприклад зміна властивості Active компонента IdTCPServer починає або припиняє опитування відповідного порту. Після встановлення зв'язку між клієнтом та сервером можна розпочинати передачу даних.

У компонентах Indy велика увага приділяється безпеці та надійності при роботі з даними. Наприклад, у компоненті IdTCPClient є методи Connect та Disconnect. Застосовуючи техніку програмування, як у наведеному нижче коді з боку клієнта:

З TCPClient do begin Connect; try lstMain.Items.Add(ReadLn); finally Disconnect; end; end;

і використовуючи властивість Connection, що передається як параметр екземпляра AThread класу TIdPeerThread, з боку сервера:

With AThread.Connection do begin WriteLn("Hello from Basic Indy Server server."); Disconnect; end;

можна розраховувати або штатне виконання з'єднання, або правильну обробку помилки.

Зверніть увагу на методи ReadLn та WriteLn відповідних класів – вони нагадують стандартні оператори введення-виведення Pascal. Це данина техніці програмування в UNIX, де більшість системних операцій виконуються завдяки читанню та запису у відповідні файли.

Так само як у компонентів Fastnet, класи компонентів Indy мають події, за допомогою яких можна організовувати подієве управління. Наприклад, можна організувати виведення повідомлення на форму при з'єднанні з клієнтом:

Procedure TForm1.IdECHOServer1Connect(AThread: TIdPeerThread); begin lblStatus.caption:= "[Serving client]"; end;

У Indy представлені компоненти, що реалізують протоколи з клієнтськими та серверними частинами, властиві лише цій бібліотеці. Компоненти TIdGopherServer і TIdGopher, завдяки методам GetExtendedMenu, GetFile, GetMenu, GetTextFile на клієнтській стороніта ReturnGopherItem, SendDirectoryEntry - на стороні сервера, допомагають здійснити перегляд файлів різного типу, у тому числі помічених як приховані, а також директорій на віддаленому комп'ютері(подібно до того, як це робить команда dir *.* в операційній системі MS-DOS).

За допомогою компонентів IdSMTP та IdMessage можна легко створити свою Web-додаток, здатну надсилати пошту за протоколом SMTP (рис. 4).

При цьому клас IdMessage (один із 23 компонентів зі сторінки Indy Misc) відповідає за формування повідомлення, що випливає з його назви, а IdSMTP - за організацію з'єднання з поштовим сервером.

Технологія, що використовується в Indy, використовує операції читання та запису з блокуванням. Будь-яка операція Connect, що використовується в Indy, очікує завершення з'єднання. При роботі з клієнтськими компонентами Indy, як правило, потрібне виконання наступних операцій:

  • запросити з'єднання із сервером;
  • здійснити запити до сервера на читання та запис (залежно від типу сервера крок виконується один раз або повторюється багато разів);
  • закінчити з'єднання з сервером та роз'єднатися.

Компоненти Indy розроблялися так, щоб забезпечити понад високий рівеньабстракції. Заплутаність і подробиці стека TCP/IP приховані від програміста, щоб він міг зосередитися на поточних завданнях.

Наступний невеликий приклад показує типову сесію клієнтського компонента:

With IndyClient do begin Host: = "zip.pbe.com"; // Host to call Port: = 6000; // Port to call the server on Connect; try // Your code goes here finally Disconnect; end; end;

У прикладі навіть якщо з'єднання з сервером не буде встановлено, зв'язок коректно розірветься завдяки використанню оператора try-finally.

Серверні компоненти Indy описують різноманітні моделі серверів, які можна використовувати залежно від потреб та протоколу, що застосовується.

TIdTCPServer - серверний компонент, що найчастіше використовується, який створює вторинний процес, незалежний від основного процесу програми. Створений процес чекає на вхідні запити від потенційних клієнтів. Для кожного клієнта, на запит якого відповідає, створюється індивідуальний вторинний процес. Події, які у процесі обслуговування, співвідносяться з контекстом відповідних процесів (рис. 5).

Іншими словами, для кожного клієнтського підключення клас TIdTCPServer використовує унікальний вторинний потік, викликаючи обробник події OnExecute цього потоку. Формальним параметром методу OnExecute є посилання на екземпляр класу Athread, що відповідає створеному потоку. Властивість Connection цього класу - посилання клас TIdTCPConnection, екземпляр якого створюється для обробки клієнтського запиту. TIdTCPConnection підтримує читання та запис через з'єднання, а також встановлення та завершення сеансу зв'язку.

Протокол UDP працює без попереднього встановлення з'єднання з сервером (кожен надісланий пакет є самостійним набором даних, а не частиною великої сесії або з'єднання). У той час, як TIdTCPServer породжує окремі потоки для кожного з'єднання, TIdUDPServer використовує або головний потік, або єдиний вторинний потік, який обробляє всі запити протоколу UDP. Коли TIdUDPServer активний, створюється потік для прослуховування вхідних UDP-пакетів. Для кожного отриманого пакету виникає подія OnUDPRead або в основному потоці, або в контексті потоку, що прослуховує - в залежності від значення властивості ThreadedEvent. Коли ThreadedEvent приймає значення False, подія виникає в основному потоці, в іншому випадку - в потоці, що прослуховує. Поки відбувається обробка події, інші операції сервера блокуються. Тому важливо стежити, щоб процедури OnUDPRead виконувались якнайшвидше.

Якщо потрібно створити клієнтську програму нового клієнта для існуючого сервера з використанням існуючого протоколу, ваше завдання полягає виключно в розробці та налагодженні клієнтської програми. Однак, коли доводиться розробляти і клієнтське, і серверне застосування із застосуванням існуючого чи нового протоколу, ми стикаємося з класичною проблемою «яйця та курки». З чого починати програмування – з клієнта чи з сервера?

Очевидно, в результаті мають бути створені і клієнт, і сервер. Для багатьох програм, що особливо використовують текстовий протокол (наприклад, HTTP), простіше розпочати створення програми з проектування сервера. А для його налагодження є зручний клієнт, який вже існує. Це - консольний додаток Telnet, який є і Windows, і UNIX.

Якщо набрати консольну команду telnet 127.0.0.1 80 з IP-адресою локального комп'ютера і номером порту 80, що використовується за замовчуванням Web-серверами, програма відгукнеться текстом, представленим на рис. 6 , у разі ОС Windows 2000 та IIS 5.0.

Для створення найпростішого сервера з використанням компонентів Indy необхідно:

  1. Створити новий проект.
  2. Помістити на головну форму проекту екземпляр компонента TIdTCPServer з панелі Indy Servers (рис. 7).
  3. Присвоїти властивості DefaultPort екземпляра класу TIdTCPServer1 значення 6002 (рекомендується надавати великі значення, щоб уникнути дублювання номерів портів у різних додатків), а властивості Active - значення True.
  4. Додати наступний текст до методу екземпляра класу TIdTCPServer1, що обробляє подію OnExecute: procedure TForm1.IdTCPServer1Execute(AThread: TIdPeerThread); var s: String; i: Integer; begin with AThread.Connection до try WriteLn("Type an integer and Enter"); s: = ReadLn; try i:= StrToInt(s); WriteLn(s + "squared is" + IntToStr(i*i)); except WriteLn(s + "is not an integer"); end; finally Disconnect; end; end;

    Методи, подібні до вищенаведеного оброблювача події, обов'язково повинні включати оператори try-finally, щоб мати можливість розірвати з'єднання з клієнтом незалежно від того, як відбувалася обробка операторів, що виконуються при з'єднанні, а також з метою коректної обробки непередбачених тимчасових затримок при з'єднанні.

  5. Скомпілюйте та збережіть проект, запустіть його або як автономну програму, або з-під Delphi, попередньо вибравши команду головного меню IDE Tools | Debugger Options і відключивши опцію Integrated debugging, що у лівому нижньому куті вікна (рис. 8).
  6. Відкрийте консольне вікно та наберіть команду telnet 127.0.0.1 6002.
  7. У вікні з'явиться текст, який створюється сервером (рис. 9).
  8. Ввівши, наприклад, цифру 9, можна побачити реакцію сервера у вигляді, показаному на рис. 10 .

    Насправді, для забезпечення синхронізації після натискання клавіші Enter у контексті клієнта на сервері відбувається так званий блокуючий виклик (тимчасова затримка на кілька мікросекунд), необхідний для забезпечення роботи з іншими можливими клієнтами. При цьому, оскільки кожен запит від клієнта обробляється незалежно і одночасно, обслуговування з боку сервера відбувається в середньому за той самий час. Якби ви програмували сервер без використання компонентів Indy, операції такого типу довелося б підтримувати самостійно.

  9. Командою exit у випадку Windows та «ctrl+]» у разі UNIX можна завершити роботу консольного вікна.
  10. Якщо під час виконання п. 5 використовувався варіант запуску з-під середовища програмування, поверніть опцію Integrated debugging у вихідний стан.

Якщо необхідно спроектувати сервер, який не тільки коректно інформуватиме своїх клієнтів про розрив з'єднання, але й видаватиме для них інформацію про помилкові ситуації, що виникли, застосовуйте оператор try-except замість try-finally - наприклад, як показано в наступному прикладі:

Procedure TDataModule1.IdTCPServer1Execute(AThread: IdPeerThread); var s: String; begin with AThread.Connection try try s:= ReadLn; // Дозволяє вирішити цю проблему // // якщо не exception is raised, // write out the server"s response WriteLn(s); except on e: Exception do begin WriteLn(e.Message); end; //on end;//try except finally Disconnect;end;end;

Цей приклад демонструє етапи створення простого текстового сервера, а також способи його налагодження.

Вищеописаний сервер типовим прикладом організації сучасного розподіленого обчислення.

Особливості створення багатоланкових додатків

Останнім часом задоволення клієнтських запитів все частіше використовуються множинні сервери. Сервер такого типу, отримавши запит клієнта та частково підготувавши його для подальшої обробки, зв'язується з іншим сервером та передає йому трансформований запит чи запити. Сервер другої ланки може зв'язуватися з іншими серверами. Таким чином, можна говорити про багатоланкову серверну архітектуру.

Далі ми займемося створенням сервера доступу до даних, призначення якого полягає в тому, щоб повертати дані з бази даних. Цей сервер, однак, не здійснює читання та запису у файли бази даних безпосередньо. Натомість він зв'язується із сервером бази даних у пошуках даних, необхідних клієнтом.

Отже, ми приступаємо до розробки програми з триланковою архітектурою. Для створення сервера бази даних із використанням компонентів Indy необхідно:

  1. Створити новий проект.
  2. Помістити на головну форму проекту екземпляр компоненту TIdTCPServer з панелі Indy Servers.
  3. Присвоїти властивості DefaultPort екземпляра класу TIdTCPServer1 значення 6001 (рекомендується надавати великі значення, щоб уникнути дублювання номерів портів у різних додатків), а властивості Active - значення true.
  4. Додати до проекту новий модуль, обравши команду File New | Data Module, і помістити на нього екземпляри компонентів SQLConnection і SQLDataSet із закладки dbExpress на панелі компонентів.
  5. Встановити властивість ConnectionName класу SQLConnection у IBLocal та LoginPrompt у False. Якщо ви не конфігурували IBLocal на базі даних employee.gdb, спершу виконайте цю процедуру.

Indy це досить потужний пакет компонентів, що дозволяє розробляти різні мережеві програми. У цьому уроці я розповім вам про те, як можна створювати клієнт-серверні програми за допомогою компонентів TIdTCPClient та TIdTCPServer.

Насамперед хочеться відзначити дві важливі переваги цих компонентів. Найголовніше з них - це багатопотоковість, яка полягає в тому, що для кожного клієнта сервер створює окремий потік, і це безумовно впливає на швидкодію серверної програми на комп'ютерах з багатоядерним процесором. Друга з переваг – це простота використання. Достатньо 10-20 рядків коду, щоб написати найпростіший клієнт-серверний додаток. Цей пакет компонентів є у стандартних збірках Delphi.

Напишемо просту програму, що дозволяє надсилати текстове повідомлення від клієнта до сервера. Приступимо до створення сервера.
Помістимо на форму компонент IdTCPServer із вкладки «Indy Servers». Всі налаштування цього компонента ми проведемо в runtime у події OnCreate форми:
IdTCPServer1.DefaultPort:= 12345;
IdTCPServer1.Active:= true;
Тут все просто – вказуємо порт, на якому працюватиме сервер, та активуємо сам сервер.

Для того, щоб отримувати дані на сервері від клієнта, існує спеціальна подія OnExecute. Виглядає ця подія так:

begin
end;

Відредагуємо вміст події так:
procedure TForm3.IdTCPServer1Execute(AContext: TIdContext);
var
l: string; // рядкова змінна, в яку ми отримуватимемо
begin
l:= AContext.Connection.IOHandler.ReadLn();
Memo1.Lines.Add(l);
end;

Тепер, як тільки на сервер надходитиме повідомлення, ми будемо його записувати в рядкову змінну l і ​​виводити в багаторядкове текстове поле.

На цьому, як не дивно, створення сервера закінчується. Решта Indy зробить за нас. Приступимо до клієнтської програми. Вона буде з'єднуватися з сервером, відсилає на нього повідомлення, і відключатися від сервера.

Створимо новий проект, помістимо на формі компонент IdTCPClient, який можна знайти на вкладці "Indy Clients". Також помістимо простий Edit та кнопку. Створимо для кнопки обробник події OnClick, усередині якої напишемо:
IdTCPClient1.Port:= 12345;
IdTCPClient1.Host:= ‘127.0.0.1’;
IdTCPClient1.Connect;
IdTCPClient1.IOHandler.WriteLn(Edit1.Text);
IdTCPClient1.Disconnect;

Цей код не обов'язково поміщати в подію OnCreate. На ваш розсуд ви можете помістити цей код будь-куди.
У першому рядку ми привласнюємо порт, причому необхідно вказати такий самий порт, який ми вказали на серверній програмі, інакше клієнт просто не знайде сервер. Потім вказуємо IP-адресу сервера. Сам сервер може бути як у локальній мережі, і віддалено. В останньому випадку з'єднання буде здійснено за допомогою Інтернету і вказувати потрібно буде IP адресу в Інтернеті.

Я вказав адресу «127.0.0.1», що говорить про те, що сервером є комп'ютер на якому запущений клієнт. Такий спосіб дуже зручний для тестування мережевих програм.
Потім ми здійснюємо підключення, відправляємо повідомлення і відключаємося. Також як і саме повідомлення, IP адресу ви можете брати теж з Edit або з будь-якої рядкової змінної.

Робота над програмою клієнтів теж закінчена. Як бачите, Indy робить за нас колосальну роботу, що дає можливість навіть недосвідченому програмісту створити свою мережеву програму.

Введення в Indy

Введення в Indy
Автор: Chad Z. Hower
Домашня сторінка: http://www.atozedsoftware.com
Переклад: Анатолій Підгорецький
Вступне слово
Я написав цю статтю, коли поточною версією Indy 8.0. Багато їх цієї статті можна застосувати і дуже корисно в наступних версіях Indy. Якщо вам сподобалася ця стаття, і ви хочете прочитати докладніші статті, то зверніть увагу на книгу Indy in Depth.
Indy працює в блокувальному режимі
Indy використовує роботу з сокетами в режимі блокування. Блокуючий режим подібний до читання-запису файлу. Під час читання даних або запису функція не повертає керування до закінчення операції. Відмінність від роботи з файлами полягає в тому, виклик може бути більш довгим, оскільки запитаних даних ще немає, це залежить від швидкості, з якою працює ваша мережа або модем.
Для прикладу, просто здійснюється виклик методу, і очікування поки не буде повернуто керування в точку виклику. Якщо виклик був успішним, то керування буде повернено з методу, при помилці буде порушено виняток.
Блокуючий режим це не смертельно
Через блокуючий режим ми неодноразово були биті нашими противниками, але блокуючий режим не є дияволом.
Проблема виникла після портування Winsock у Windows. У Юнікс типово проблема вирішувалася за рахунок роздвоєння (схоже на багато потоковість, але за рахунок окремих процесів замість потоків). Юнікс клієнти і демони (daemons) мали роздвоюватися процеси, які мали виконуватися і використовувати блокуючий режим. Windows 3.x не міг розпаралелювати і не підтримував багато потоковість. Використання блокуючого інтерфейсу заморожувало інтерфейс і робило програми не реагують. Тому до WinSock були додані режими, що не блокують, дозволяючи Windows 3.x з його обмеженнями використовувати Winsock без блокування основного і єдиного потоку програми. Це зажадало іншого програмування, Microsoft та інші пристрасно поносили блокуючі режими, щоб приховати недоліки Windows 3.x.
Потім прийшла Win32, яка змогла підтримати багато потоків. Але до цього моменту мізки вже були запудрені (тобто розробники вважали блокуючі сокети породженням диявола), і вже було важко змінити вчинене. Тому паплюження блокуючих режимів триває.
Насправді в Юнікс є лише блокуючі сокети. Блокуючі сокети також мають свої переваги, і вони набагато краще, для багато потоковості, безпеки та інших аспектів. Деякі розширення були додані і в Юнікс для не блокуючих сокетів. Тим не менш, вони працюють зовсім інакше, ніж у Windows. Вони також нестандартні та не дуже поширені. Блокуючі сокети в Юнікс використовуються майже у всіх випадках і будуть і надалі використовуватися.
Переваги блокуючого режиму Простіше програмувати - Блокуючі режими простіше програмувати. Весь код користувача може знаходитися в одному місці і виконуватися в природному, послідовному порядку. ·Простіше перенесення в Юнікс - Оскільки Юнікс використовує блокуючі сокети, код, що переноситься, написати в даному випадку простіше. Indy використовує цей факт для написання єдиного коду. В·Зручніше працювати з потоками - Оскільки у блокуючих сокетів послідовність придбана у спадковості, тому їх дуже просто використовувати в потоках.
Недоліки блокуючого режиму · Інтерфейс користувача заморожується в клієнтах - Виклик блокуючого сокету не повертає управління, поки не виконає своє завдання. Коли такий виклик здійснюється в головному потоці програми, програма не може обробляти повідомлення користувача. Через це користувальницький інтерфейс заморожується, не оновлюються вікна, та інші повідомлення не можуть бути оброблені доки керування не буде повернуто з блокуючого сокету.
Компонент TIdAntiFreeze
Indy має спеціальний компонент, який вирішує проблему заморожування. інтерфейсу користувача. Просто додайте один компонент TIdAntiFreeze куди не будь у своїй програмі, і ви зможете виконувати блокуючі виклики без заморожування інтерфейсу користувача.
TIdAntiFreeze працює за внутрішнім таймером поза стеком викликів і викликає Application.ProcessMessages після закінчення таймууту. Зовнішні виклики Indy продовжують залишатися блокуючими і тому працюють так само як і без використання компонента TIdAntiFreeze. Використання TIdAntiFreeze дозволяє отримати всі переваги блокуючих сокетів, без недоліків.
Кодові потоки (Threading)
З блокуючими сокетами майже завжди використовуються кодові потоки. Не блокуючі сокети можуть використовувати потоки, але це вимагає деякої додаткової обробки і їх переваги в цьому випадку губляться, в порівнянні з блокуючими сокетами.
Переваги потоків·Настроювання пріоритетів - Пріоритети окремих потоків можуть бути налаштовані. Це дозволяє виконувати виділяти окремим завданням більше чи менше процесорного часу. В· Інкапсуляція - Кожне з'єднання може містити деяку подібність інтерфейсу з іншим з'єднанням. В·Безпека - Кожен потік може мати різні атрибути безпеки. В·Кілька процесорів - дає перевагу на системах з декількома процесорами. В· Не потрібна серіалізація - надає повну конкурентність. Без багато потоковості всі запити мають бути опрацьовані в одному потоці. Тому кожне завдання має бути розбите на невеликі шматки, щоб воно могло працювати швидко. Поки виконується один блок, решта змушені чекати його закінчення. Після закінчення одного блоку виконується наступний і так далі. З багатопоточністю кожна задача може бути запрограмована як одне ціле і операційна системарозподіляє час між усіма завданнями.
Опитування потоків
Створення та знищення потоків дуже інтенсивно використовує ресурси. Це особливо важке завдання для серверів, які мають короткоживуть з'єднання. Кожен сервер створює потік, використовує його короткий час, а потім знищує. Це призводить до дуже частого створення та видалення потоків. Прикладом цього є сервер. Надсилається одиночний запит, і повертається проста відповідь. При використанні браузера, при перегляді будь-якого веб-сайту, можуть відбуватися сотні з'єднань і від'єднань
Опитування потоків може виправити цю ситуацію. Замість створення та знищення потоків на вимогу, потоки вибираються зі списку невикористовуваних, але вже створених потоків з пулу. Коли потік не потрібен, він повертається в пул, замість його знищення. Потоки в пулі позначаються як такі, що не використовуються і тому вони не жеруть процесорний час. Для поліпшення потоки можуть динамічно підлаштовуватися під поточні потреби системи.
Indy підтримує опитування потоків. Пул потоків Indy доступний через компонент TIdThreadMgrPool.
Безліч потоків
Для сильно навантаженого сервера може знадобитися сотні чи навіть тисячі потоків. Є загальне переконання, що сотні та тисячі потоків можуть вбити вашу систему. Це неправильне переконання.
У більшості серверів потоки знаходяться в очікуванні даних. Під час очікування при блокуванні виклику потік неактивний. У сервері з 500 потоків тільки 50 можуть бути активні одночасно.
Кількість потоків, запущених на вашій системі, може здивувати вас. При мінімальній кількості запущених серверів та вказаними запущеними програмамимоя система має 333 створені потоки, навіть при 333 потоках процесор навантажений тільки на 1%. Сильно навантажений сервер IIS(Microsoft Internet Information Server) може створити сотні та тисячі потоків.
Потоки та глобальні секції
При кількох потоках необхідно забезпечити цілісність даних при доступі до них. Це може бути складним для програмістів, які не працювали з потоками. Але, як правило, більшості серверів не потрібне використання глобальних даних. Більшість серверів виконує ізольовані функції. Кожен потік виконує своє ізольоване завдання. Глобальні секції читання/запису – це особливість багатьох багатопотокових додатків, але не типові для серверів.
Методологія Indy
Indy відрізняється від інших Winsock компонентів, до яких Ви звикли. Якщо ви працювали з іншими компонентами, то найкращим рішенням буде забути, як вони працюють. Багато інших компонентів використовують неблокуючі (асинхронні) виклики та працюють асинхронно. Їм потрібно реагувати на події, створювати машину стану та часто виконувати цикли очікування.
Наприклад, з іншими компонентами, коли ви викликає з'єднання ви повинні або чекати виникнення події з'єднання або в циклі очікувати коли властивість покаже, що з'єднання сталося. З Indy ви можете викликати метод Connect і чекати повернення з нього. Повернення буде здійснено у разі успішного з'єднання або порушення винятку у разі проблеми. Тому робота з Indy дуже схожа на роботу з файлами. Indy дозволяє розмістити весь ваш код в одному місці замість розмазування по різних подіях. На додаток Indy дуже простий і найбільш зручний при роботі з потоками.
Наскільки Indy відрізняється
Короткий огляд·Використовуються блокуючі дзвінки·Не орієнтований на події - події є, але вони використовуються для інформаційних потреб, і реально не потрібні. В· Розроблений для потоків - Indy розроблений для потоків, тим не менш, може використовуватися і без потоків. ·Послідовне програмування
Детальний розгляд
Indy не тільки використовує блокуючі дзвінки (синхронні), але ще й працює так. Типова сесія в Indy виглядає так:
with IndyClient do begin
Connect; Try
// Do your stuff here
finally Disconnect; end;
end;
З іншими компонентами це виглядає так:
procedure TFormMain.TestOnClick(Sender: TComponent);
begin
with SocketComponent do begin
Connect; try
while not Connected do begin
if IsError then begin
Abort;
end;

OutData:= "Data To send";
while length(OutData) > 0 do begin
Application.ProcessMessages;
end;
finally Disconnect; end;
end;
end;
procedure TFormMain.OnConnectError;
begin
IsError:= True;
end;
procedure TFormMain.OnRead;
var
i: Integer;
begin
i:= SocketComponent.Send(OutData);
OutData: = Copy (OutData, i + 1, MaxInt);
end;
Багато компонентів не дуже добре виконують роботу із ізоляції програміста від стека. Багато компонентів замість ізоляції користувача від складнощів стека просто залишають його наодинці з ним або надають обгортку над стеком.
Особливий шлях Indy
Indy розроблений з нуля бути багатопотоковим. Побудова серверів і клієнтів в Indy подібна до побудови серверів і клієнтів в Юнікс. Юнікс програми зазвичай викликають стек безпосередньо з мінімумом або зовсім без шару абстрагування.
Зазвичай Юнікс сервера мають один або кілька слухачів, які стежать за вхідними запитами клієнтів. Для кожного клієнта, якого потрібно обслужити, створюється новий процес. Це робить простим програмування, кожен процес лише для одного клієнта. Кожен процес запускається у своєму власному безпековому контексті, який задається слухаючим процесом або процесом, що базується на існуючих правах, ідентифікації або інших речах.
Indy сервери працюють майже аналогічно. Windows на відміну від Юнікс не може добре розмножувати процеси, зате добре працює з потоками. Indy сервер створюють окремий потік для кожного з'єднання клієнта.
Indy сервера призначають потік, що слухає, який відокремлений від головного кодового потоку програми. Потік слухання слухає вхідні запити від клієнтів. Для кожного клієнта, якому відповідає, створюється новий потік обслуговування клієнта. Відповідні події потім обслуговуються у контексті даного потоку.
Огляд клієнтів Indy
Indy розроблений щоб надати дуже високий рівень абстракції. Заплутаність і деталізація TCP/IP стека ховається від програміста. Зазвичай типова сесія клієнта в Indy виглядає так:
with IndyClient do begin
Host:="zip.pbe.com"; // Host to call
Port: = 6000; // Port to call the server on
Connect; Try
// Do your stuff here
finally Disconnect; end;
end;
Огляд серверів Indy
Indy серверні компоненти створюють потік, що слухає, який ізольований від головного кодового потоку програми. Потік, що слухає, прослуховує вхідні запити від клієнтів. Для кожного клієнта, якому відповідає, створюється новий потік обслуговування клієнта. Відповідні події потім обслуговуються в контексті потоку.

Практичні приклади
Наступні приклади повинні допомогти вам почати працювати з компонентами для простого використання, але для того, щоб продемонструвати приклади зроблено як прості програми. Деякі проекти зроблено для демонстрації різноманітних ситуацій. Дані приклади також доступні для завантаження як файли zip.
Примітка від перекладача: посилання на веб-сайті не робоче.
Приклад 1 - Перевірка поштового індексу
Перший проект зроблено максимально простим. Пошук за поштовим індексом, клієнт запитує сервер якого міста та штату належить зазначений індекс.
Для тих хто живе за межами США і не знають, що таке zip код, це поштовий код, який вказує місце доставки. Поштові коди складаються із 5 цифр.
Протокол
Перший крок у побудові сервера та клієнта – це розробка протоколу. Для стандартних протоколів визначається відповідним RFC. Для поштового індексу протокол визначається нижче.
Більшість протоколів обміну працюють у текстовому режимі. Обмін означає, що передається команда, а у відповідь стан і можливі дані. Протоколи не обмежуються обміном, але використовується простий текст. Протокол визначення поштового індексу також текстовий. Простий текст робить протоколи простими для налагодження та дозволяє спілкуватися різними мовами програмування та операційними системами.
Після з'єднання сервер надсилає вітальне повідомлення, потім приймає команду. Ця команда може бути "ZipCode x" (Де x це поштовий індекс) або "Quit". У відповідь на команду ZipCode надсилається відповідь у вигляді одного рядка з відповіддю або порожній рядок, якщо код не знайдено. Команда Quit змушує сервер розірвати з'єднання. Сервер може прийняти кілька команд, перш ніж буде надіслано команду Quit.
Вихідний код сервера

unit ServerMain;

interface

uses

type

TformMain = class (TForm)

IdTCPServer1: TIdTCPServer;

procedure FormCreate(Sender: TObject);

procedure FormDestroy(Sender: TObject);

procedure IdTCPServer1Connect(AThread: TIdPeerThread) ;

private

ZipCodeList: TStrings;

public

end;

FormMain: TformMain;

implementation

(R*.DFM)

procedure TformMain.IdTCPServer1Connect (AThread: TIdPeerThread) ;

begin

AThread.Connection .WriteLn ("Indy Zip Code Server Ready.") ;

end;

SCommand: string;

begin

SCommand: = ReadLn;

end;

end;

end;

procedure TformMain.FormCreate (Sender: TObject);

begin

ZipCodeList:= TStringList.Create ;

ZipCodeList.LoadFromFile (ExtractFilePath (Application.EXEName) + "ZipCodes.dat");

end;

procedure TformMain.FormDestroy (Sender: TObject);

begin

ZipCodeList.Free;

end;

end.

Єдині частини у проекті, специфічні для Indy, це компонент IdTCPServer1, методи IdTCPServer1Connect та IdTCPServer1Execute.
На формі розміщено компонент IdTCPServer1 типу TIdTCPServer. Змінено такі властивості: Active = True - Після старту програми сервер прослуховує. В· DefaultPort = 6000 - Значення порту для даного проекту. Сервер слухає запити клієнта у цьому порту.
Метод IdTCPServer1Execute пов'язаний з подією OnExecute сервера. Подія OnExecute порушується після того, як з'єднання клієнта є акцепційним. Подія OnExecute відрізняється від інших відомих вам подій. OnExecute виконується в контексті потоку. Подія потоку викликається і передається аргумент AThread, переданий у метод. Це важливо, оскільки безліч подій OnExecute може виконуватися одночасно. Це зроблено для того, щоб сервер міг працювати без створення нового компонента. Існують також методи, які можуть бути перекриті при побудові спадкоємців.
Подія OnConnect викликається після того, як з'єднання було акцепцировано і потік для нього було створено. У даному серверіце використовується для надсилання вітального повідомлення клієнту. За бажанням це також може бути виконано і в події OnExecute.
Подія OnExecute може бути порушена кілька разів, доки з'єднання не буде роз'єднано або втрачено. Цим усувається необхідність перевіряти з'єднання, щодо роз'єднання чи втрати у циклі всередині події.
IdTCPServer1Execute використовує дві базові функції, ReadLn та WriteLn. ReadLn читає рядок зі з'єднання, а WriteLn посилає рядок з'єднання.
sCommand: = ReadLn;
Вище наведений код приймає рядок від клієнта і поміщає його до локальної рядкової змінної sCommand.

if SameText (sCommand, "QUIT" ) then begin

end else if SameText (Copy (sCommand, 1 , 8 ) , "ZipCode ") then begin

WriteLn (ZipCodeList.Values ​​[Copy(sCommand, 9, MaxInt)]));

end;


Далі sCommand перевіряється на допустимі команди.
Якщо команда "Quit" виконується Роз'єднання. Жодне читання або запис не дозволено після роз'єднання. Після закінчення події слухаючий потік більше його не викликає, а очищає потік і припиняє з'єднання.
Якщо ж команда "ZipCode", то параметр після команди витягується та переглядається таблиця щодо наявності міста та штату. Місто і штат потім передаються клієнту або передається порожній рядок якщо немає відповідності.
Далі відбувається вихід із методу. Сервер повторить виклик події знову, як тільки надійде нова команда, дозволяючи клієнту надсилати безліч команд.
Вихідний код клієнта

unit ClientMain;

interface

uses

Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,

StdCtrls, ExtCtrls, IdAntiFreezeBase,

IdAntiFreeze, IdBaseComponent, IdComponent, IdTCPConnection, IdTCPClient;

type

TformMain = class (TForm)

Client: TIdTCPClient;

IdAntiFreeze1: TIdAntiFreeze;

Panel1: TPanel;

Panel2: TPanel;

MemoInput: TMemo;

LboxResults: TListBox;

Panel3: TPanel;

Button1: TButton;

Button2: TButton;

Label1: TLabel;

procedure Button2Click(Sender: TObject);

procedure Button1Click(Sender: TObject);

private

public

end;

FormMain: TformMain;

implementation

(R*.DFM)

procedure TformMain.Button2Click (Sender: TObject);

begin

MemoInput.Clear;

LboxResults.Clear;

end;

procedure TformMain.Button1Click (Sender: TObject);

I: integer;

S: string;

begin

ButnLookup.Enabled := true ; try

LboxResults.Clear;

with Client do begin

Connect; try

LboxResults.Items .Add (ReadLn);

for i:= 0 to memoInput.Lines .Count - 1 do begin

WriteLn ("ZipCode" + memoInput.Lines [i]);

LboxResults.Items .Add (memoInput.Lines [i]);

S:= ReadLn;

if s = "" then begin

S:= "-- No entry found for this zip code.";

end;

LboxResults.Items .Add(s) ;

LboxResults.Items .Add ("") ;

end;

WriteLn ("Quit");

finally Disconnect; end;

end;

finally butnLookup.Enabled := true; end;

end;

end.


Єдині частини специфічні клієнтського компонента - це метод Button1Click.
Компонент Client типу TIdTCPClient та розміщений на формі. Змінені такі характеристики: · Host = 127.0.0.1 - Сервер знаходиться на тій же машині, що і клієнт. ·Port = 6000 - Порт сервера
Метод Button1Click пов'язані з подією OnClick компоненти Button1. При натисканні кнопки викликається цей метод. Indy частина цього методу може бути зменшена до наступного: 1.З'єднання з сервером (Connect;) 1.Читання привітання з сервером. 1.Для кожного рядка введеного користувачем у TMemo: 1.Посилання запиту на сервер (WriteLn("ZipCode " + memoInput.Lines[i]);) 1.Читання відповіді з сервера (s:= ReadLn;) 1.Посилання команди Quit (WriteLn("Quit");) 1. Роз'єднання (Disconnect;)
Тестування
Цей приклад був протестований і працює за встановленого TCP/IP. Ви можете змінити його для роботи через мережу з одного комп'ютера до іншого. Запустивши сервер на іншому комп'ютері та змінивши ім'я або IP-сервера на клієнті.
Для тестування проектів відкомпілюйте та запустіть сервер. Потім відкомпілюйте та запустіть клієнта. Введіть поштовий індекс у поле і натисніть клавішу lookup.
Налагодження
Текстові протоколи дуже просто налагоджувати, оскільки вони можуть бути перевірені за допомогою Телнет. Для цього достатньо знати порт сервера. Zip Code Lookup Server слухає порту 6000.
Запустіть Zip Code Lookup Server. Відкрийте консоль (наприклад, вікно Dos). Тепер введіть:
telnet 127.0.0.1 6000
Тепер ви з'єдналися із сервером. Деякі сервери при цьому надсилають вітальне повідомлення. Дехто цього не робить. Ви не побачите рядків, які ви вводите. Більшість серверів не роблять відлуння, з метою економії трафіку. Тим не менш, ви можете змінити налаштування телнет, налаштування "Echo On". У різних телнет клієнтах це робиться по-різному, а ряд взагалі не мають такої можливості. Тепер введіть:
zipcode 37642
Ви побачите відповідь сервера:
CHURCH HILL, TN
Для від'єднання від сервера введіть:
quit
Приклад 2 – доступ до бази даних
Даний приклад емулює сервер, який повинен виконувати блокуючі завдання, інші, ніж виклики сокетів. Багато серверів змушені працювати в таких умовах. Сервера які потребують звернення до бази, виклики зовнішніх процедур або розрахунків часто не можуть перервати ці виклики, оскільки це зовнішні виклики або через складність цього. Звернення до бази не може бути розбите на маленькі шматки і розробник повинен чекати на закінчення операції з базою. Це є особливість не тільки звернень до бази даних, але й іншими операціями, такими як стиснення, розрахунки та інша обробка того ж роду.
Для цілей демонстрації, уявімо, що сервер робить звернення до бази, яка потребує 5 секунд для виконання. Для спрощення виконаємо це просто за допомогою паузи, використовуємо для цього функцію Sleep (5000) замість реального звернення.
Цей приклад також вимагає менше детальності, ніж попередній приклад, оскільки багато концепцій поки що не зрозумілі.
Вихідний код

unit main;

interface

uses

Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,

IdBaseComponent, IdComponent, IdTCPServer;

type

TformMain = class (TForm)

IdTCPServer1: TIdTCPServer;

procedure IdTCPServer1Execute(AThread: TIdPeerThread) ;

private

public

end;

FormMain: TformMain;

implementation

(R*.DFM)

procedure TformMain.IdTCPServer1Execute (AThread: TIdPeerThread) ;

I: integer;

begin

with AThread.Connection do begin

WriteLn ("Hello. DB Server ready.");

I:= StrToIntDef (ReadLn, 0);

// Sleep is substituted for long DB or other call

Sleep (5000);

WriteLn (IntToStr (i * 7));

end;

end;

end.

Оскільки подія Execute виникає у контексті потоку, код обробки може бути будь-якої довжини. Кожен клієнт має власний потік і не блокує інших клієнтів.
Тестування
Для тестування DB сервера, відкомпілюйте та запустіть його. З'єднайтеся з ним за допомогою Телнет на порт 6001. Сервер відповість вітальним повідомленням. Введіть номер. Сервер "обробить" ваш запит і відповість через 5 секунд.

Сподобалася стаття? Поділіться з друзями!
Чи була ця стаття корисною?
Так
Ні
Дякую за ваш відгук!
Щось пішло не так і Ваш голос не був врахований.
Дякую. Ваше повідомлення відправлено
Знайшли у тексті помилку?
Виділіть її, натисніть Ctrl+Enterі ми все виправимо!