Програмування сокетів в Дельфи, Різне, Програмування, статті

Введення


Дана стаття присвячена створенню додатків архітектури клієнт / сервер в Borland Delphi на основі сокетів (“sockets” – гнізда). А написав я цю статтю не просто так, а тому що останнім часом це питання дуже багатьох став цікавити. Поки що торкнемося лише створення клієнтської частини сокетного програми.


Вперше я познайомився з сокетами, якщо не помиляюся, рік чи півтора тому. Тоді стояло завдання розробити прикладної протокол, який би передавав на серверну машину (працює на ОС Unix / Linux) запит і отримував відповідь по сокетного каналу. Треба зауважити, що на відміну від будь-яких інших протоколів (FTP, POP, SMTP, HTTP, і т.д.), сокети – це база для цих протоколів. Таким чином, користуючись сокетами, можна самому створити (зімітувати) і FTP, і POP, і будь-який інший протокол, причому не обов’язково вже створений, а навіть свій власний!


Отже, почнемо з теорії. Якщо Ви переконаний практик (і в очі не можете бачити всяких алгоритмів), то Вам слід пропустити цей розділ.

Алгоритм роботи з сокетних протоколами


Так що ж дозволяють нам робити сокети? … Та все що завгодно! І в цьому одна з головних достоїнств цього способу обміну даними в мережі. Справа в тому, що при роботі з сокетом Ви просто посилаєте іншого комп’ютера послідовність символів. Так що цим методом Ви можете посилати як прості повідомлення, так і цілі файли! Причому, контролювати правильність передачі Вам не потрібно (як це було при роботі з COM-портами)!


Нижче слід приблизна схема роботи з сокетами в Дельфи-додатках

Розберемо схему детальніше:



Опис властивостей і методів компонента TClientSocket


Тут ми познайомимося з основними властивостями, методами і подіями компонента TClientSocket.













Властивості


Методи


Події

   Active – Показує, відкритий сокет чи ні. Тип: Boolean . Відповідно, True – відкритий, а False – закритий. Це властивість доступно для запису;
   Host – Рядок (Тип: string ), Яка вказує на хост-ім’я комп’ютера, до якого слід підключитися;
   Address – Рядок (Тип: string ), Що вказує на IP-адреса комп’ютера, до якого слід підключитися. На відміну від Host , Тут може міститися лише IP. Відмінність в тому, що якщо Ви вкажете в Host символьне ім’я комп’ютера, то IP адреса, що відповідає цьому імені буде запитаний у DNS;
   Port – Номер порта (Тип: Integer (Word) ), До якого слід підключитися. Допустимі значення – від 1 до 65535;
   Service – Рядок (Тип: string ), Що визначає службу (ftp, http, pop, І т.д.), до порту якої відбудеться підключення. Це своєрідний довідник відповідності номерів портів різним стандартним протоколам;
   ClientType – Тип з’єднання. ctNonBlocking – асинхронна передача даних, тобто посилати і приймати дані по сокету можна за допомогою OnRead і OnWrite. ctBlocking – синхронна (одночасна) передача даних. Події OnRead і OnWrite не працюють. Цей тип з’єднання корисний для організації обміну даними за допомогою потоків (тобто робота з сокетом як з файлом);
 
   Open – Відкриття сокета (аналогічно привласненню значення True властивості Active);
   Close – Закриття сокета (аналогічно привласненню значення False властивості Active);

На цьому всі методи компонента TClientSocket вичерпуються. А Ви запитаєте: “А як же працювати з сокетом? Як тоді пересилати дані?”. Про це Ви дізнаєтесь трохи далі.

   OnConnect – Як випливає з назви, ця подія виникає при встановленні з’єднання. Тобто в обробнику цієї події вже можна починати авторизацію або прийом / передачу даних;
   OnConnecting – Виникає при встановленні з’єднання. Відмінність від OnConnect в тому, що з’єднання ще не встановлено. Зазвичай такі проміжні події використовуються для оновлення статусу;
   OnDisconnect – Виникає при закритті сокета. Причому, закриття як з Вашої програми, так і з стронони віддаленого комп’ютера (або через збій);
   OnError – Продовжує сумну тему попереднього події :). Виникає при помилку в роботі сокета. Слід зазначити, що ця подія не допоможе Вам відловити помилку в момент відкриття сокета (Open). Для того, щоб уникнути видачі Віндозного повідомлення про помилку, треба укласти оператори відкриття сокета в блок try..except (Обробка виняткових ситуацій);
   OnLookup – Виникає при спробі отримання від DNS IP-адреси зазначеного хоста;
   OnRead – Виникає, коли віддалений комп’ютер послав Вам будь-які дані. При виникненні цієї події можлива обробка даних;
   OnWrite – Виникає, коли Вам дозволена запис даних в сокет.
 

Практика і приклади


Найлегше (і корисніше) вивчати будь-які методи програмування на практиці. Тому далі наведено приклади з деякими коментарями:








Приклад 1. Найпростіша сокетних програма
{… Тут йде заголовок файлу і визначення форми TForm1 і її примірника Form1}

{У форму потрібно помістити кнопку TButton і два TEdit. При натисканні на кнопку викликається обробник події OnClick – Button1Click. Перед цим у перший з TEdit-ов потрібно ввести хост-ім’я, а в другій – порт віддаленого комп’ютера.
НЕ ЗАБУДЬТЕ помістити у форму КОМПОНЕНТ TClientSocket!}
procedure Button1Click(Sender: TObject);
begin
{Надаємо властивостям Host і Port потрібні значення}
  ClientSocket1.Host := Edit1.Text;
  ClientSocket1.Port := StrToInt(Edit2.Text);
{Намагаємося відкрити сокет і встановити з’єднання}
  ClientSocket1.Open;
end;

procedure ClientSocket1Connect(Sender: TObject; Socket: TCustomWinSocket);
begin
{Як тільки відбулося з’єднання – закриваємо сокет і перериваємо зв’язок}
  ClientSocket1.Close;
end;


Якщо Ви думаєте, що даний приклад програми абсолютно даремний і не може принести ніякої користі, то глибоко помиляєтеся. Наведений код – найпростіший приклад сканера портів (PortScanner). Суть такої утиліти в тому, щоб перевіряти, чи включений вказаний порт і чи готовий він до прийому / передачі даних. Саме на такому принципі засновано PortScanner з програми NetTools Pro.


Далі слідує інший приклад, в якому по сокету передаються і приймаються текстові повідомлення:








Приклад 2. Здійснення / прийом текстових повідомлень по сокетів
{… Тут йде заголовок файлу і визначення форми TForm1 і її примірника Form1}

{У форму потрібно помістити дві кнопки TButton і три TEdit. При натисканні на першу кнопку викликається обробник події OnClick – Button1Click. Перед цим у перший з TEdit-ов потрібно ввести хост-ім’я, а в другій – Порт віддаленого комп’ютера. Після встановлення з’єднання можна посилати текстові повідомлення, вводячи текст в третій TEdit і натискаючи другу кнопку TButton. Щоб від’єднатися, потрібно ще раз натиснув першу TButton. Ще потрібно додати TListBox, в який будемо поміщати прийняті і відправлені повідомлення.
НЕ ЗАБУДЬТЕ помістити у форму КОМПОНЕНТ TClientSocket!}
procedure Button1Click(Sender: TObject);
begin
{Якщо з’єднання вже встановлено – перериваємо його.}
  if ClientSocket1.Active then begin
    ClientSocket1.Close;
Exit; {… і виходимо з обробника}
  end;
{Надаємо властивостям Host і Port потрібні значення}
  ClientSocket1.Host := Edit1.Text;
  ClientSocket1.Port := StrToInt(Edit2.Text);
{Намагаємося відкрити сокет і встановити з’єднання}
  ClientSocket1.Open;
end;

procedure ClientSocket1Connect(Sender: TObject; Socket: TCustomWinSocket);
begin
{Як тільки відбулося з’єднання – посилаємо вітання}
  Socket.SendText(Hello!);
  ListBox1.Items.Add(< Hello!);
end;

procedure ClientSocket1Read(Sender: TObject; Socket: TCustomWinSocket);
begin
{Якщо прийшло повідомлення – додаємо його в ListBox}
  ListBox1.Items.Add(> +Socket.ReceiveText);
end;

procedure Button2Click(Sender: TObject);
begin
{Натиснуто кнопка – посилаємо текст з третього TEdit}
  ClientSocket1.Socket.SendText(Edit3.Text);
  ListBox1.Items.Add(< +Edit3.Text);
end;


ПРИМІТКА: В деяких випадках (залежать від сервера) потрібно після кожного повідомлення посилати переклад рядки:
  ClientSocket1.Socket.SendText(Edit3.Text+#10);

Робота з сокетних потоком


“А як ще можна працювати з сокетом?”, – Запитаєте Ви. Природно, наведений вище метод – не найкраще рішення. Самих методів організації роботи з сокетами дуже багато. Я наведу лише ще один додатковий – Робота через потік. Напевно, багато хто з Вас вже мають досвід роботи, якщо не з потоками (stream), то з файлами – точно. Для тих, хто не знає, потік – це канал для обміну даними, робота з яким аналогічна роботі зі звичайним файлом. Наведений нижче приклад показує, як організувати потік для роботи з сокетом:








Приклад 3. Потік для роботи з сокетом
procedure ClientSocket1Connect(Sender: TObject; Socket: TCustomWinSocket);
 var c: Char;
        MySocket: TWinSocketStream;
begin
{Як тільки відбулося з’єднання – створюємо потік і асоціюємо його з сокетом (60000 – таймаут в мсек)}
  MySocket := TWinSocketStream.Create(Socket,60000);
{Оператор WaitForData чекає даних з потоку вказаний час в мсек (в даному прикладі – 100) і повертає True, якщо отриманий хоча б один байт даних, False – якщо немає ніяких даних з потоку.}
  while not MySocket.WaitForData(100) do
    Application.ProcessMessages;
{Application.ProcessMessages дозволяє Windows перемалювати потрібні елементи вікна і дає час іншим програмам. Якщо б цього оператора не було і дані б досить довго не надходили, то система б злегка “підвисла”.}
  MySocket.Read(c,1);
{Оператор Read читає вказану кількість байт з потоку (в даному прикладі – 1) у вказану змінну певного типу (у прикладі – в змінну c типу Char). Зверніть увагу на те, що Read, в відміну від ReadBuffer, не встановлює строгих обмежень на кількість прийнятої інформації. Тобто Read читає не більше n байтів з потоку (де n – вказане число). Ця функція повертає кількість отриманих байтів даних.}
  MySocket.Write(c,1);
{Оператор Write аналогічний оператору Read, за тим лише виключенням, що Write пише дані в потік.}
  MySocket.Free;
{Не забудемо звільнити пам’ять, виділену під потік}
end;

ПРИМІТКА: Для використання потоку не забудьте встановити властивість ClientType в ctBlocking.


Здійснення / прийом складних даних


Іноді необхідно пересилати по мережі не тільки прості текстові повідомлення, а й складні структури (тип record в Паскалі), або навіть файли. І тоді Вам необхідно використовувати спеціальні оператори. Деякі з них перераховані нижче:


Методи TClientSocket.Socket (TCustomWinSocket, TClientWinSocket) :



Всім перерахованим методам відповідають методи Receive … Їх опис можна подивитися в довідковому файлі по Дельфі (VCL help).

Авторизація на сервері


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








Приклад 4. Авторизація
{У даному прикладі потрібно додати в форму ще два TEdit – Edit3 і Edit4 для введення логіна і пароля}
procedure ClientSocket1Connect(Sender: TObject; Socket: TCustomWinSocket);
 var c: Char;
        MySocket: TWinSocketStream;
        login,password: string;
begin
  MySocket := TWinSocketStream.Create(Socket,60000);
{Додаємо до логіну та паролю символ перекладу рядка, щоб сервер зміг відокремити логін і пароль.}
  login := Edit3.Text+#10;
  password := Edit4.Text+#10;
  MySocket.Write(login,Length(Edit3.Text)+1);
  MySocket.Write(password,Length(Edit4.Text)+1);
  while not MySocket.WaitForData(100) do
    Application.ProcessMessages;
  MySocket.Read(c,1);
{Тут сервер посилає нам один байт, значення 1 якого відповідає підтвердження успішної авторизації, а 0 – помилку (це лише приклад). Далі ми виконуємо потрібні дії (прийом / пересилання даних) і закриваємо потік.}
  MySocket.Free;
end;

Епілог


У цій статті я написав лише зовсім небагато з того, що можна було б сказати про сокети. Може бути, коли в мене з’явиться новий приплив сил, я доповню цю статтю ще більш цікавим матеріалом.

Схожі статті:


Сподобалася стаття? Ви можете залишити відгук або підписатися на RSS , щоб автоматично отримувати інформацію про нові статтях.

Коментарів поки що немає.

Ваш отзыв

Поділ на параграфи відбувається автоматично, адреса електронної пошти ніколи не буде опублікований, допустимий HTML: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>

*

*