Клієнт TCP - Банк рефератів, творів, доповідей, курсових та дипломних робіт

Сервери та клієнти

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

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

Спочатку розберемо простий приклад - отримання документа за протоколом HTTP. Існує спеціальний інтерфейс, що полегшує роботу з сокетами з Perl - IO::Socket. Однак ми не будемо використовувати IO::Socket, тому що ми бажаємо отримати максимальний контроль над з'єднанням. Хоча реально, використання IO::Socket значно полегшує роботу з сокетами, та ще й значно знижує ризик припуститися помилки. При вирішенні реальних завдань рекомендую використовувати IO::Socket.

Отже, я назвав програму "gethttp.pl". Намзнадобиться модуль Socket, в якому реалізовані всі необхідні платформозалежні функції. Насамперед, це стосується функції ідентифікації сокетів. Але все по порядку.

die "Usage: gethttp.pl [host] [document] n";

my $sock_name = GetSockName($ARGV[0],80)

or die "Couldn't convert $ARGV[0] в Internet address: $!n";

or die "Couldn't' connect to $ARGV[0]: $!n";

print CONN "GET $ARGV[1] HTTP/1.0n";

print CONN "Host: $ARGV[0]nn";

Після підключення модуля Socket ми перевіряємо, чи достатньо аргументів передано на вхід програми. Згідно з описом, запуск програми має аргументуватися двома значеннями: ім'ям хоста та віртуальним шляхом одержуваного документа. Так от, якщо масив вхідних аргументів не містить двох аргументів, тоді програма завершується.

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

Після ідентифікації сокету, ми створюємо сокет за допомогою вбудованої функції socket(). Як аргументи ця функція приймає дескриптор з'єднання (на замітку: звичайний файловий маніпулятор), константу, що визначає область дії сокету (PF_INET або PF_UNIX), константу, що визначає тип сокету (датаграмний або потоковий) та ідентифікатор протоколу. Ідентифікатор протоколу, що відповідає встановленому в системі, визначається числовим значенням в.за допомогою функції getprotobyname().

Після створення сокета ми намагаємося з'єднатися з віддаленим сервером. Робиться це за допомогою функції connect(), якаргументів якої приймається дескриптор та ідентифікатор сокету. У разі невдалої спроби з'єднання програма завершується з помилкою. Далі оператори print, які за правилами протоколу HTTP оголошують необхідність видати зазначений документ. У масиві @body ми зберігаємо дані, отримані від сервера. Закриваємо з'єднання та виводимо вміст масиву @body. Ось наш документ.

Тепер розберемо функцію GetSockName().

return undef unless defined($nm);

return undef unless defined($pt);

return undef unless $nm = gethostbyname($nm);

Тепер спробуймо, що ж у нас вийшло. Запускаємо програму та. Ха-ха-ха. Що знову зависли? Ну, і де ж у нас помилка? Правильно, ми забули вимкнути буферизацію. Дані ніби відправлені, але їх дуже мало для заповнення буфера. Тому фактично наші відправлені дані застрягли в сокеті. А сервер чекає і чекає, коли клієнт замовить про своє бажання. Давайте виправимо положення і додамо наступний код між рядками:

or die "Couldn't' connect to $ARGV[0]: $!n";

select(CONN); $ = 1; select(STDOUT);

print CONN "GET $ARGV[1] HTTP/1.0n";

Ось тепер все має працювати.

Наша програма, звичайно, не супер-пупер, але на дещо вона таки згодиться. Наприклад, якщо відокремити заголовки відповіді від безпосередньо тіла відповіді, то можна завантажувати файли по HTTP. Давайте так і зробимо.

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

print CONN "GET $ARGV[1] HTTP/1.0n";

print CONN "Host: $ARGV[0]nn";

Тепер ми можемо виконати команду:

[root@avalon scripts]# ./gethttp.pl whirlwind /index.cgi > index.html

для отримання HTML-коду індексної сторінки нашого сайту.

Та й усе, для початку. У вас вже достатньо навичок для метушні з протоколами високого рівня. Ось і спробуйте попрацювати з FTP або SMTP.