Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
практична робота 10.doc
Скачиваний:
0
Добавлен:
01.07.2025
Размер:
196.1 Кб
Скачать

Приклад програми udp-серверу

Оскільки UDP-сервер використовує ті ж самі системні виклики, що і UDP-клієнт, ми можемо відразу приступити до розгляду прикладу UDP-серверу (програма 15–16-2.с) для сервісу echo.

/* Простий приклад UDP-серверу для сервісу echo */

#include <sys/types.h>

#include <sys/socket.h>

#include <netinet/in.h>

#include <arpa/inet.h>

#include <string.h>

#include <stdio.h>

#include <errno.h>

#include <unistd.h>

int main()

{

int sockfd; /* Дескриптор сокета */

int clilen, n; /* Змінні для різних довжин і кількості символів */

char line[1000]; /* Масив для прийнятої і посиланого рядка */

struct sockaddr_in servaddr, cliaddr; /* Структури для адрес серверу і клієнта */

/* Заповнюємо структуру для адреси серверу: сімейство протоколів TCP/IP, мережний інтерфейс – будь-хто, номер порту 51000. Оскільки в структурі міститься додаткове не потрібне нам поле, яке повинне бути нульовим, перед заповненням обнуляємо її всю */

bzero(&servaddr, sizeof(servaddr));

servaddr.sin_family = AF_INET;

servaddr.sin_port = htons(51000);

servaddr.sin_addr.s_addr = htonl(INADDR_ANY);

/* Створюємо UDP сокет */

if((sockfd = socket(PF_INET, SOCK_DGRAM, 0)) < 0){

реrror(NULL); /* Друкуємо повідомлення про помилку */

exit(1);

}

/* Настроюємо адресу сокета */

if(bind(sockfd (struct sockaddr *) &servaddr

sizeof(servaddr)) < 0){

реrror(NULL);

close(sockfd);

exit(1);

}

while(1){

/* Основний цикл обслуживания*/

/* В змінну clilen заносимо максимальну довжину для очікуваної адреси клієнта */

clilen = sizeof(cliaddr);

/* Чекаємо приходу запиту від клієнта і читаємо його. Максимальна допустима довжина датаграми – 999 символів, адресу відправника поміщаємо в структуру cliaddr, його реальна довжина буде занесена в змінну clilen */

if((n = recvfrom(sockfd, line, 999, 0

(struct sockaddr *) &cliaddr &clilen)) < 0){

реrror(NULL);

close(sockfd);

exit(1);

}

/* Друкуємо прийнятий текст на екрані */

printf("%s\n", line);

/* Прийнятий текст відправляємо назад за адресою відправника */

if(sendto(sockfd, line, strlen(line), 0

(struct sockaddr *) &cliaddr, clilen) < 0){

реrror(NULL);

close(sockfd);

exit(1);

} /* Йдемо чекати нову датаграму*/

}

return 0;

}

Лістинг 15-16.2. Програма 15–16-2.c . Простий приклад UDP-серверу для сервісу echo.

Наберіть і відкомпілюйте програму.

Організація зв'язку між процесами за допомогою установки логічного з'єднання

Тепер подивимося, які дії нам знадобляться для організації взаємодії процесів за допомогою протоколу TCP, тобто за допомогою створення логічного з'єднання. І почнемо, як і в розділі "Використовування моделі клієнт-сервер для взаємодії видалених процесів" поточного семінару, з простої життєвої аналогії. Якщо взаємодія процесів через датаграминагадує спілкування людей по листуванню, то для протоколу TCP кращою аналогією є спілкування людей по телефону.

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

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

Якщо дві люди розмовляють по телефону, то спроба інших людей додзвонитися до них виявиться невдалою. Йтиме сигнал "зайнято", і з'єднання не встановиться. В той же час хотілося б, щоб клієнт в такій ситуації не діставав відмову в обслуговуванні, а чекав своєї черги. Подібне спостерігається в різних телефонних довідкових, коли ви чуєте "Чекайте, будь ласка, відповіді. Вам обов'язково відповість оператор". Тому наступна дія серверу – це створення черги для обслуговування клієнтів. Далі сервер повинен дочекатися встановлення з'єднання, прочитати інформацію, передану по лінії зв'язку, обробити її і відправити одержаний результат назад. Обмін інформацією може здійснюватися неодноразово. Помітимо, що сокет, що знаходиться в пасивному стані, не призначений для операцій прийому і передачі інформації. Для спілкування на сервері під час встановлення з'єднання автоматично створюється новий потоковий сокет, через який і проводиться обмін даними з клієнтами. Після закінчення спілкування сервер "кладе трубку" (закриває цей новий сокет) і відправляється чекати чергового дзвінка.

Схемно ці дії виглядають так, як показано на рисунку 15–16.7. Як і у разі протоколу UDP окремим діям або їх групам відповідають системні виклики, частково співпадаючі з викликами для протоколу UDP. Їх назви написані праворуч від блоків відповідних дій.

Для протоколу TCP нерівноправність процесів клієнта і серверу видна особливо виразно у відмінності системних викликів, що використовуються. Для створення сокетів і там, і там як і раніше використовується системний виклик socket(). Потім набори системних викликів стають різними.

Для прив'язки серверу до IP-адреси і номера порту, як і у разі UDP- протоколу, використовується системний виклик bind(). Для процесу клієнта ця прив'язка з'єднана з процесом встановлення з'єднання з сервером в новому системному виклику connect() і прихована від очей користувача. Усередині цього виклику операційна система здійснює настройку сокета на вибраний нею порт і на адресу будь-якого мережного інтерфейсу. Для перекладу сокета на сервері в пасивний стан і для створення черги з'єднань служить системний виклик listen(). Сервер чекає з'єднання і одержує інформацію про адресу клієнта, що з'єднався з ним, за допомогою сі стемного виклику accept(). Оскільки встановлене логічне з'єднання виглядає з боку процесів як канал зв'язку, що дозволяє обмінюватися даними за допомогою потокової моделі, для передачі і читання інформації обидва системні виклики використовують вже відомі нам системні виклики read() і write(), а для завершення з'єднання – системний виклик close(). Необхідно відзначити, що при роботі з сокетами виклики read() і write() володіють тими ж особливостями поведінки, що і при роботі з pip’ами і FIFO (див. семінар 5).

Мал. 15-16.7.  Схема взаємодії клієнта і серверу для протоколу TCP