ПЕРЕЛІК ДИСЦИПЛІН:
  • Адміністративне право
  • Арбітражний процес
  • Архітектура
  • Астрологія
  • Астрономія
  • Банківська справа
  • Безпека життєдіяльності
  • Біографії
  • Біологія
  • Біологія і хімія
  • Ботаніка та сільське гос-во
  • Бухгалтерський облік і аудит
  • Валютні відносини
  • Ветеринарія
  • Військова кафедра
  • Географія
  • Геодезія
  • Геологія
  • Етика
  • Держава і право
  • Цивільне право і процес
  • Діловодство
  • Гроші та кредит
  • Природничі науки
  • Журналістика
  • Екологія
  • Видавнича справа та поліграфія
  • Інвестиції
  • Іноземна мова
  • Інформатика
  • Інформатика, програмування
  • Юрист по наследству
  • Історичні особистості
  • Історія
  • Історія техніки
  • Кибернетика
  • Комунікації і зв'язок
  • Комп'ютерні науки
  • Косметологія
  • Короткий зміст творів
  • Криміналістика
  • Кримінологія
  • Криптология
  • Кулінарія
  • Культура і мистецтво
  • Культурологія
  • Російська література
  • Література і російська мова
  • Логіка
  • Логістика
  • Маркетинг
  • Математика
  • Медицина, здоров'я
  • Медичні науки
  • Міжнародне публічне право
  • Міжнародне приватне право
  • Міжнародні відносини
  • Менеджмент
  • Металургія
  • Москвоведение
  • Мовознавство
  • Музика
  • Муніципальне право
  • Податки, оподаткування
  •  
    Бесплатные рефераты
     

     

     

     

     

     

         
     
    Програма-клієнт
         

     

    Інформатика, програмування

    Програма-клієнт

    Робота з WinSocket

    Socket (гніздо, роз'єм) - абстрактне програмне поняття, що використовується для позначення в прикладній програмі кінцевої точки каналу зв'язку з комунікаційним середовищем, утвореної обчислювальною мережею. При використанні протоколів TCP/IP можна говорити, що socket є засобом підключення прикладної програми до порту (див. вище) локального вузла мережі.

    Socket-інтерфейс являє собою просто набір системних викликів та/або бібліотечних функцій мови програмування СІ, розділених на чотири групи:

    1. Локального управління

    2. Встановлення зв'язку

    3. Обміну даними (введення/виводу)

    4. Закриття зв'язку

    5. Приклад використання WinSocket

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

    1. Опції локального управління

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

    1.1 Створення socket'а

    Створення socket'а здійснюється наступним системним викликом

    # include int socket (domain, type, protocol) int domain, int type; int protocol;

    Аргумент domain задає використовуваний для взаємодії набір протоколів (вид комунікаційної області), для стека протоколів TCP/IP він повинен мати символьне значення AF_INET (визначено в sys/socket.h).

    Аргумент type задає режим взаємодії:

    SOCK_STREAM - з встановленням з'єднання;

    SOCK_DGRAM - без встановлення з'єднання.

    Аргумент protocolзадает конкретний протокол транспортного рівня (з декількох можливих у стеку протоколів). Якщо цей аргумент задано рівним 0, то буде використаний протокол "за замовчуванням" (TCP для SOCK_STREAM і UDP для SOCK_DGRAM при використанні комплекту протоколів TCP/IP).

    При вдалому завершенні своєї роботи ця функція повертає дескриптор socket'а - ціле невід'ємне число, однозначно його ідентифікує. Дескриптор socket'а аналогічний дескриптор файлу ОС UNIX.

    При виявленні помилки в ході своєї роботи функція повертає число "-1".

    1.2. Зв'язування socket'а

    Для підключення socket'а до комунікаційне середовище, утвореної обчислювальної мережі, необхідно виконати системний виклик bind, визначальними у прийнятому для мережі форматі локальний адресу каналу зв'язку з середовищем. У мережах TCP/IP socket зв'язується з локальним портом. Системний виклик bind має наступний синтаксис:

    # include

    # include # include int bind (s, addr, addrlen) int s; struct sockaddr * addr; int addrlen;

    Аргумент s задає дескриптор пов'язують socket'а.

    Аргумент addr в загальному випадку повинен вказувати на структуру даних, яка містить локальний адресу, що приписується socket'у. Для мереж TCP/IP такою структурою є sockaddr_in.

    Аргумент addrlen задає розмір (в байтах) структури даних, що вказується аргументом addr.

    Структура sockaddr_in використовується декількома системними викликами та функціями socket-інтерфейсу і визначено в include-файлі in.h наступним чином:

    struct sockaddr_in (Short sin_family; u_short sin_port; struct in_addr sin_addr; char sin_zero [8];);

    Поле sin_family визначає використовуваний формат адреси (набір протоколів), у нашому випадку (для TCP/IP) вона повинна мати значення AF_INET.

    Поле sin_addr містить адресу (номер) вузла мережі.

    Поле sin_port містить номер порту на вузлі мережі.

    Поле sin_zero не використовується.

    Визначення структури in_addr (з того ж include-файлу) таке:

    struct in_addr ( union (u_long S_addr; / *

    інші (не цікавлять нас)

    члени об'єднання */

    ) S_un; # define s_addr S_un.S_addr);

    Структура sockaddr_in повинна бути повністю заповнена перед видачею системного виклику bind. При цьому, якщо поле sin_addr.s_addr має значення INADDR_ANY, то системний виклик буде прив'язувати до socket'у номер (адреса) локального вузла мережі.

    У разі успіху bind повертає 0, інакше - "-1".

    2. Функції встановлення зв'язку

    Для встановлення зв'язку "клієнт-сервер" використовуються системні виклики listen і accept (на стороні сервера), а також connect (на стороні клієнта). Для заповнення полів структури socaddr_in, використовуваної у виклику connect, звичайно використовується бібліотечна функція gethostbyname, що транслює символічне ім'я вузла мережі в його номер (адреса).

    2.1. Очікування встановлення зв'язку

    Системний виклик listen висловлює бажання видала його програми-сервера очікувати запити до неї від програм-клієнтів і має наступний вигляд:

    # include int listen (s, n) int s; int n;

    Аргумент s задає дескриптор socket'а, через який програма буде очікувати запити до неї від клієнтів. Socket повинен бути попередньо створений системним викликом socketі забезпечений адресою з допомогою системного виклику bind.

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

    Ознакою вдалого завершення системного виклику listen служить нульовий код повернення.

    2.2. Запит на встановлення з'єднання

    Для звернення програми-клієнта до сервера з запитом на встановлення логічної з'єднання використовується системний виклик connect, має такий вигляд

    # include

    # include # include int connect (s, addr, addrlen) int s; struct sockaddr_in * addr; int addrlen;

    Аргумент s задає дескриптор socket'а, через який програма звертається до сервера з запитом на з'єднання. Socket повинен бути попередньо створений системним викликом socketі забезпечений адресою з допомогою системного виклику bind.

    Аргумент addr повинен вказувати на структуру даних, містить адресу, приписаний socket'у програми-сервера, до якої робиться запит на з'єднання. Для мереж TCP/IP такою структурою є sockaddr_in. Для формування значень полів структури sockaddr_in зручно використовувати функцію gethostbyname.

    Аргумент addrlen задає розмір (в байтах) структури даних, що вказується аргументом addr.

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

    При успішному виконанні запиту системний виклик connect повертає 0, в іншому випадку - "-1" (встановлюючи код причини неуспіху в глобальній змінній errno).

    Примітка. Якщо до моменту виконання connect використовуваний ним socket не був прив'язаний до адреси за допомогою bind, то така прив'язка буде виконана автоматично.

    Примітка. У режимі взаємодії без встановлення з'єднання необхідності у виконанні системного виклику connect немає. Однак, його виконання в такому режимі не є помилкою - просто міняється зміст виконуваних при цьому дій: встановлюється адреса "за замовчуванням" для всіх наступних посилок дейтаграм.

    2.3. Прийом запиту на встановлення зв'язку

    Для прийому запитів від програм-клієнтів на встановлення зв'язку в програмах-серверах використовується системний виклик accept, має такий вигляд:

    # include

    # include int accept (s, addr, p_addrlen) int s; struct sockaddr_in * addr; int * p_addrlen;

    Аргумент s задає дескриптор socket'а, через який програма-сервер отримала запит на з'єднання (за допомогою системного запиту listen).

    Аргумент addr повинен вказувати на область пам'яті, розмір якої дозволяв би розмістити в ній структуру даних, що містить адресу socket'а програми-клієнта, яка зробила запит на з'єднання. Ніякої ініціалізації цій області не потрібно.

    Аргумент p_addrlen повинен вказувати на область пам'яті у вигляді цілого числа, що задає розмір (в байтах) області пам'яті, що вказується аргументом addr.

    Системний виклик accept витягує з черги, організованою системним викликом listen, перший запит на з'єднання і повертає дескриптор нового (автоматично створеного) socket'а з тими ж властивості, що й socket, що задається аргументом s. Цей новий дескриптор необхідно використовувати у всіх подальших операціях обміну даними.

    Крім того після вдалого завершення accept:

    область пам'яті, що вказується аргументом addr, буде містити структуру даних (для мереж TCP/IP це sockaddr_in), що описує адреса socket'а програми-клієнта, через який вона зробила свій запит на з'єднання;

    ціле число, на яке вказує аргумент p_addrlen, дорівнюватиме розміру цієї структури даних.

    Якщо чергу запитів на момент виконання accept порожня, то програма переходить в стан очікування надходження запитів від клієнтів на невизначений час (хоча така поведінка accept можна й змінити).

    Ознакою невдалого завершення accept служить негативне повернене значення (дескриптор socket'а негативним бути не може).

    Примітка. Системний виклик accept використовується в програмах-серверах, що функціонують тільки в режимі з встановленням з'єднання.

    2.4. Формування адреси вузла мережі

    Для отримання адреси вузла мережі TCP/IP за його символічному імені використовується бібліотечна функція

    # include

    # include struct hostent * gethostbyname (name) char * name;

    Аргумент name задає адреса послідовності літер, утворять символічне ім'я вузла мережі.

    При успішному завершенні функція повертає вказівник на структуру hostent, визначену у include-файлі netdb.h і має наступний вид

    struct hostent ( char * h_name; char ** h_aliases; int h_addrtype; int h_lenght; char * h_addr;);

    Поле h_name вказує на офіційне (основне) ім'я вузла.

    Поле h_aliases вказує на список додаткових імен вузла (синонімів), якщо вони є.

    Поле h_addrtype містить ідентифікатор використовується набору протоколів, для мереж TCP/IP це поле буде мати значення AF_INET.

    Поле h_lenght містить довжину адреси вузла.

    Поле h_addr вказує на область пам'яті, що містить адреса вузла в тому вигляді, в якому його використовують системні виклики та функції socket-інтерфейсу.

    Приклад звернення до функції gethostbyname для отримання адреси віддаленого вузла в програмі-клієнті, що використовує системний виклик connect для формування запиту на підтримання зв'язку з програмою-сервером на цьому сайті, розглядається нижче.

    3. Функції обміну даними

    У режимі зі встановленням логічного з'єднання після вдалого виконання пари взаємопов'язаних системних викликів connect (у клієнта) і accept (в сервер) стає можливим обмін даними.

    Цей обмін може бути реалізований звичайними системними викликами read і write, що використовуються для роботи з файлами (при цьому замість дескрипторів файлів у них задаються дескриптори socket'ов).

    Крім того можуть бути додатково використані системні виклики send і recv, спеціально орієнтовані на роботу з socket'амі.

    Примітка. Для обміну даними в режимі без встановлення логічного з'єднання використовуються, як правило, системні виклики sendtoі recvfrom. Sendto дозволяє специфікувати разом з передаваними даними (складовими дейтаграму) адреса їх одержувача. Recvfrom одночасно з доставкою даних одержувачу інформує його і про адресу відправника.

    3.1. Здійснення даних

    Щоб здійснити даних партнеру з мережевого взаємодії використовується системний виклик send, що має такий вигляд

    # include

    # include int send (s, buf, len, flags) int s; char * buf; int len; int flags;

    Аргумент s задає дескриптор socket'а, через який надсилаються дані.

    Аргумент buf вказує на область пам'яті, що містить передані дані.

    Аргумент len задає довжину (у байтах) передаються даних.

    Аргумент flags модифікує виконання системного виклику send. При нульовому значенні цього аргументу виклик send повністю аналогічний системного виклику write.

    При успішному завершенні send повертає кількість переданих з області, зазначеної аргументом buf, байт даних. Якщо канал даних, який визначається дескриптором s, виявляється "переповненим", то send переводить програму в стан очікування до моменту його звільнення.

    3.2. Отримання даних

    Для отримання даних від партнера по мережному взаємодії використовується системний виклик recv, що має такий вигляд

    # include

    # include int recv (s, buf, len, flags) int s; char * buf; int len; int flags;

    Аргумент s задає дескриптор socket'а, через який приймаються дані.

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

    Аргумент len задає довжину (у байтах) цієї області.

    Аргумент flags модифікує виконання системного виклику recv. При нульовому значенні цього аргументу виклик recv повністю аналогічний системного виклику read.

    При успішному завершенні recv повертає кількість прийнятих в область, зазначену аргументом buf, байт даних. Якщо канал даних, обумовлений дескриптором s, виявляється "порожнім", то recv переводить програму в стан очікування до моменту появи в ньому даних.

    4. Опції закриття зв'язку

    Для закриття зв'язку з партнером по мережному взаємодії використовуються системні виклики close і shutdown.

    4.1. Виклик close

    Для закриття раніше створеного socket'а використовується звичайний системний виклик close, що застосовується в ОС UNIX для закриття раніше відкритих файлів і має такий вигляд

    int close (s) int s;

    Аргумент s задає дескриптор раніше створеного socket'а.

    Проте в режимі з встановленням логічного з'єднання (що забезпечує, як правило, надійну доставку даних) внутрішньосистемні механізми обміну будуть намагатися передати/прийняти дані, що залишилися в каналі передачі на момент закриття socket'а. Це може тривати значний інтервал часу, неприйнятний для деяких додатків. У такій ситуації необхідно використовувати описуваний далі системний виклик shutdown.

    4.2. Скидання буферізованние даних

    Для "екстреного" закриття зв'язку з партнером (шляхом "скидання" ще не переданих даних) використовується системний виклик shutdown, що виконується перед close і має такий вигляд

    int shutdown (s, how) int s; int how;

    Аргумент s задає дескриптор раніше створеного socket'а.

    Аргумент how задає дії, що виконуються при очищенні системних буферів socket'а:

    0 - скинути і далі не брати дані для читання з socket'а;

    1 - скинути і далі не відправляти дані для посилки через socket;

    2 - скинути всі дані, передані через socket в будь-якому напрямку.

    5. Приклад використання socket-інтерфейсу

    У цьому розділі розглядається використання socket-інтерфейсу в режимі взаємодії зі встановленням логічного з'єднання на дуже простому прикладі взаємодії двох програм (сервера і клієнта), що функціонують у різних вузлах мережі TCP/IP.

    Змістовна частина програм примітивна:

    сервер, прийнявши запит на з'єднання, передає клієнту питання "Who are you?";

    клієнт, отримавши питання, виводить його в стандартний висновок і направляє серверу відповідь "I am your client" і завершує на цьому свою роботу;

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

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

    5.1. Програма-сервер

    Текст програми-сервера на мові програмування СІ виглядає наступним чином

    1 # include

    2 # include

    3 # include

    4 # include

    5 # include

    6 # define SRV_PORT 1234

    7 # define BUF_SIZE 64

    8 # define TXT_QUEST "Who are you? n "

    9 main () (

    10 int s, s_new;

    11 int from_len;

    12 char buf [BUF_SIZE];

    13 struct sockaddr_in sin, from_sin;

    14 s = socket (AF_INET, SOCK_STREAM, 0);

    15 memset ((char *) & sin,'', sizeof (sin));

    16 sin.sin_family = AF_INET;

    17 sin.sin_addr.s_addr = INADDR_ANY;

    18 sin.sin_port = SRV_PORT;

    19 bind (s, (struct sockaddr *) & sin, sizeof (sin));

    20 listen (s, 3);

    21 while (1) (

    22 from_len = sizeof (from_sin);

    23 s_new = accept (s, & from_sin, & from_len);

    24 write (s_new, TXT_QUEST, sizeof (TXT_QUEST));

    25 from_len = read (s_new, buf, BUF_SIZE);

    26 write (1, buf, from_len);

    27 shutdown (s_new, 0);

    28 close (s_new);

    29);

    30)

    Рядки 1 ... 5 описують включаються файли, що містять визначення для всіх необхідних структур даних і символічних констант.

    Рядок 6 приписує цілочисельний константі 1234 символічне ім'я SRV_PORT. Надалі ця константа буде використана в як номер порту сервера. Значення цієї константи має бути відомо і програми-клієнта.

    Рядок 7 приписує цілочисельний константі 64 символічне ім'я BUF_SIZE. Ця константа буде визначати розмір буфера, використовують?? мого для розміщення приймаються від клієнта даних.

    Рядок 8 приписує послідовності символів, складових текст питання клієнту, символічне ім'я TXT_QUEST. Останнім символом у послідовності є символ переходу на новий рядок 'n'. Зроблено це для спрощення перегляду тексту питання на стороні клієнта.

    У рядку 14 створюється (відкривається) socket для організації режиму взаємодії зі встановленням логічного з'єднання (SOCK_STREAM) у мережі TCP/IP (AF_INET), при виборі протоколу транспортного рівня використовується протокол "за умовчанням" (0).

    У рядках 15 ... 18 спочатку обнуляється структура даних sin, а потім заповнюються її окремі поля. Використання константи INADDR_ANY спрощує текст програми, позбавляючи від необхідності використовувати функцію gethostbyname для отримання адреси локального вузла, на якому запускається сервер.

    Рядок 19 за допомогою системного виклику bind прив'язує socket, що задається дескриптором s, до порту з номером SRV_PORT на локальному вузлі. Bind завершиться успішно за умови, що в момент його виконання на тому ж сайті вже не функціонує програма, що використовує цей номер порту.

    Рядок 20 за допомогою системного виклику listen організовує чергу на три входять до сервера запиту на з'єднання.

    Рядок 21 служить заголовком нескінченного циклу обслуговування запитів від клієнтів.

    На рядку 23, що містить системний виклик accept, виконання програми призупиняється на невизначений час, якщо чергу запитів до сервера на встановлення зв'язку виявляється порожня. При появі такого запиту accept успішно завершується, повертаючи в змінної s_new дескриптор socket'а для обміну інформацією з клієнтом.

    У рядку 24 сервер з допомогою системного виклику write відправляє клієнту питання.

    У рядку 25 за допомогою системного виклику read читається відповідь клієнта.

    У рядку 26 відповідь направляється у стандартний висновок, що має дескриптор файлу номер 1. Так як рядок відповіді містить в собі символ переходу на новий рядок, то текст відповіді буде розміщений на окремому рядку дисплея.

    Рядок 27 містить системний висновок shutdown, забезпечує очищення системних буферів socket'а, що містять дані для читання ( "зайві" дані можуть там опинитися в результаті невірної роботи клієнта).

    У рядку 28 закривається (вилучається) socket, використаний для обміну даними з черговим клієнтом.

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

    Текст програми-клієнта на мові програмування СІ виглядає наступним чином

    1 # include

    2 # include

    3 # include

    4 # include

    5 # include

    6 # define SRV_HOST "delta"

    7 # define SRV_PORT 1234

    8 # define CLNT_PORT 1235

    9 # define BUF_SIZE 64

    10 # define TXT_ANSW "I am your clientn "

    11 main () (

    12 int s;

    13 int from_len;

    14 char buf [BUF_SIZE];

    15 struct hostent * hp;

    16 struct sockaddr_in clnt_sin, srv_sin;

    17 s = socket (AF_INET, SOCK_STREAM, 0);

    18 memset ((char *) & clnt_sin,'', sizeof (clnt_sin));

    19 clnt_sin.sin_family = AF_INET;

    20 clnt_sin.sin_addr.s_addr = INADDR_ANY;

    21 clnt_sin.sin_port = CLNT_PORT;

    22 bind (s, (struct sockaddr *) & clnt_sin, sizeof (clnt_sin));

    23 memset ((char *) & srv_sin,'', sizeof (srv_sin));

    24 hp = gethostbyname (SRV_HOST);

    25 srv_sin.sin_family = AF_INET;

    26 memcpy ((char *) & srv_sin.sin_addr, hp-> h_addr, hp-> h_length);

    27 srv_sin.sin_port = SRV_PORT;

    28 connect (s, & srv_sin, sizeof (srv_sin));

    29 from_len = recv (s, buf, BUF_SIZE, 0);

    30 write (1, buf, from_len);

    31 send (s, TXT_ANSW, sizeof (TXT_ANSW), 0);

    32 close (s);

    33 exit (0);

    34)

    У рядках 6 і 7 описуються константи SRV_HOST і SRV_PORT, що визначають ім'я віддаленого вузла, на якому функціонує програма-сервер, і номер порту, до якого прив'язаний socket сервера.

    Рядок 8 приписує цілочисельний константі 1235 символічне ім'я CLNT_PORT. Надалі ця константа буде використана в як номер порту клієнта.

    У рядках 17 ... 22 створюється прив'язаний до порту на локальному вузлі socket.

    У рядку 24 за допомогою бібліотечної функції gethostbyname транслюється символічне ім'я віддаленого вузла (в даному випадку "delta"), на якому має функціонувати сервер, на адресу цього вузла, розміщений у структурі типу hostent.

    В рядку 26 адреса віддаленого вузла копіюється з структури типу hostent у відповідне поле структури srv_sin, яка пізніше (у рядку 28) використовується в системному виклику connect для ідентифікації програми-сервера.

    У рядках 29 ... 31 здійснюється обмін даними з сервером і висновок питання, що надійшов від сервера, в стандартний вивід.

    Рядок 32 за допомогою системного виклику close закриває (видаляє) socket.

    Список літератури

    Для підготовки даної роботи були використані матеріали з сайту http://www.realcoding.net/

         
     
         
    Реферат Банк
     
    Рефераты
     
    Бесплатные рефераты
     

     

     

     

     

     

     

     
     
     
      Все права защищены. Reff.net.ua - українські реферати ! DMCA.com Protection Status