logo
делать №5

Контрольні питання:

  1. Поясніть основні підходи до розпаралелення задачі сортування масивів?

  2. Чим відрізняється сортування масивів для одно- та багатопроцесорних систем.

  3. Поясніть принцип сортування масиву з 5-ти чисел за допомогою двох процесорів?

  4. Чому при збільшенні кількості процесорів до 3-х кількість кроків (час виконання програми) для сортування масиву з 5-ти чисел не змінеться?

  5. Що таке мютекс: призначення, особливості та галузі використання?

  6. Які операції існують для роботи з мютексами?

  7. Чим мютекс відрізняється від бінарного семафору?

Лабораторна робота №5

Тема: «Розподілені обчислення»

Мета роботи: ознайомитися з принципами організації розподілених обчислень та механізмом сокетів зокрема на прикладі вирішення задачі перевірки ізоморфності графів

Задача

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

Сокети

Найрозповсюдженим методом обміну повідомленнями є вико­ристання сокетів (sockets). Ця технологія насамперед призначена для організації мережного обміну даними, але може бути вико­ристана й для взаємодії між процесами на одному комп’ютері (власне, мережну взаємодію можна розуміти як узагальнення IPC).

Сокет — це абстрактна кінцева точка з’єднання, через яку процес може відсилати або отримувати повідомлення. Обмін даними між двома процесами здійснюють через пару сокетів, по одному на кожен процес. Абстрактність сокету полягає в тому, що він приховує особливості реалізації передачі повідомлень — після того як сокет створений, робота з ним не залежить від технології пере­дачі даних, тому один і той самий код можна без великих змін використовувати для роботи із різними протоколами зв’язку.

Особливості протоколу передачі даних і формування адреси сокету визначає комунікаційний домен — його потрібно зазначити під час створення кожного сокету. Прикладами доменів можуть бути домен Інтернету (який задає протокол зв’язку на базі TCP/IP) і локальний домен або домен UNIX, що реалізує зв’язок із використанням імені файлу (подібно до поіменованого каналу). Сокет можна використовувати у поєднанні тільки з одним кому­нікаційним доменом. Адреса сокету залежить від домену (наприклад, для сокетів домену UNIX такою адресою буде ім’я файлу).

Способи передачі даних через сокет визначаються його типом. У конкретному домені можуть підтримуватися або не підтримува­тися різні типи сокетів. Наприклад, і для домену Інтернету, і для домену UNIX підтримуються сокети таких типів:

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

Перед тим як почати працювати з сокетами, будь-який процес (і клієнт, і сервер) має створити сокет за допомогою системного виклику socket(). Параметрами цього виклику є комунікаційний домен і тип сокету. Цей виклик повертає дескриптор сокету — унікальне значення, за яким можна буде звертатися до цього сокету.

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

Сокет пов’язують з адресою за допомогою системного виклику bind(). Для сокетів домену UNIX як адресу задають ім’я файлу, для сокетів домену Інтернету — необхідні характеристики мережного з’єднання. Далі клієнт для встановлення з’єднання й обміну повідомленнями має вказати саме цю адресу.

Сервер дає змогу клієнтам встановлювати з’єднання, виконав­ши системний виклик listen() для дескриптора сокету, створеного раніше.

Після виходу із системного виклику listen() сервер готовий приймати від клієнтів запити на з’єднання. Ці запити вишико­вуються в чергу. Для отримання запиту із цієї черги і створення з’єднання використовують системний виклик accept(). Внаслідок його виконання у використання повертають новий сокет для обміну даними із клієнтом. Старий сокет можна використовувати далі для приймання нових запитів для з’єднання. Якщо під час виклику accept() запити на з’єднання в черзі відсутні, сервер переходить у стан очікування.

Для клієнта послідовність дій після створення сокету зовсім інша. Замість трьох кроків досить виконати один — встановлення з’єднання із використанням системного виклику connect(). Парамет­рами цього виклику задають дескриптор створеного раніше сокету, а також адресу, подібну до вказаної на сервері для виклику bind().

Після встановлення з’єднання (і на клієнті, і на сервері) з’я­виться можливість передавати і приймати дані з використанням цього з’єднання. Для передачі даних застосовують системний виклик send(), а для приймання — recv().

Зазначену послідовність кроків використовують для встанов­лення надійного з’єднання. Якщо все, що нам потрібно — це ві­діслати і прийняти конкретне повідомлення фіксованої довжини, то з’єднання можна й не створювати зовсім. Для цього як відправник, так і одержувач повідомлення мають попередньо зв’язати сокети з адресами через виклик bind(). Потім можна скористатися викликами прямої передачі даних: sendto() — для відправника і recvfrom() — для одержувача. Параметрами цих викликів є адреси одержу­вача і відправника, а також адреси буферів для даних.

Листинг програми клієнта

#include <iostream.h>

#include <windows.h>

#include <conio.h>

#pragma comment (lib, "ws2_32.lib")

#pragma comment (lib, "mswsock.lib")

void main(void) {SetConsoleOutputCP(1251); //Використання кодування 1251

char PCName[30], ServerName[30], IP[16] = { 0 };

char a[8],Message[42];

int Messag0e[200], i,j;

WSAData WSADat; // Властивості WinSock (результат функції WSAStartup)

sockaddr_in sin; // Властивості (адреса) сокету, що створюється

SOCKET Sock; // Клієнтський сокет

// Введення IP-адреси сервера

cout << "Enter server's IP: ";

cin.getline(IP, 16);

WSAStartup(0x0202, &WSADat); // Ініціалізація WinSock

// 0x0202 - версія WinSock. Може бути 1.0, 1.1, 2.0, 2.2

// WSADat - структура, куди будуть занесені рез. ініціалізації

gethostname(PCName, 30); // Отримання імені поточного ПК

sin.sin_family = AF_INET; // Тип адреси

sin.sin_addr.s_addr = inet_addr(IP); // IP-адреса серверу (при створенні серверу 0)

sin.sin_port = htons(2803); // Номер порту сервера

Sock = socket(AF_INET, SOCK_STREAM, 0); // Створення сокету

// ***** Підключення до серверу

cout << "Connecting to server..." << endl;

if (connect(Sock, (sockaddr*)&sin, sizeof(sin)) == SOCKET_ERROR) {

cout << "Error of connecting!\n";

goto End; }

send(Sock, PCName, strlen(PCName) + 1, 0); // Відправка імені комп'ютера (клієнта)

recv(Sock, ServerName, 30, 0); // Отримання імені сервера

cout << "Connecting to \"" << ServerName << "\" is ready!" << endl;

// Відправка повідомлення серверу

cout<<"введіть масив А\n"; //Ініціалізація масиву А

for (i=0;i<4;i++){

cin>>a[i]; }

cout<<"введіть масив B\n"; //Ініціалізація масиву В

for (i=4;i<8;i++){

cin>>a[i]; }

if (send(Sock, a, strlen(a) + 1, 0) != SOCKET_ERROR) cout << "Sent!\n";

else cout << "Error of sending!\n";

cout << endl;

recv(Sock,Message, 42, 0); // Отримання імені сервера

for (i=1;i<3;i++){cout<<Message;cin.get();}

End: // Закриття сокетів і завершення роботи с WinSock

closesocket(Sock);

WSACleanup();

getch();

}

Лістинг програми сервера

#include <iostream.h>

# include <stdio.h>

#include <windows.h>

#include <conio.h>

char flag[2]="0";

#pragma comment (lib, "ws2_32.lib")

#pragma comment (lib, "mswsock.lib")

int flag1=0;

int porivn (char Mes[8])

{int i=0,flag=1;

for (i=0;i<4;i++){

if (Mes[i]!=Mes[i+4]) flag=0;

}

if (flag==1) {printf("%s","da");flag1=1;};

cout<<endl;

return 0;

}

int perestX(char Mes[8])

{ char a;

int i=0;

for (i=0;i<2;i++){

a=Mes[4+i];

Mes[4+i]=Mes[6+i];

Mes[6+i]=a;}

return 0;

}

int perestY(char Mes[8])

{ char a;

int i=0;

for (i=0;i<2;i++){

a=Mes[4+i];

Mes[4+i]=Mes[5+i];

Mes[5+i]=a;

i++;}

return 0;

}

void main(void) {

char PCName [30], ClientName[30],Message[200];

char ne[42]={'м','а','т','р','и','ц','і',' ','і','з','о','м','о','р','ф','н','і','!',' ',' ',' '};

char nr[42]={'м','а','т','р','и','ц','і',' ','н','е',' ','і','з','о','м','о','р','ф','н','і','!'};

int i,j;

WSAData WSADat; // Властивості WinSock (результат функції WSAStartup)

sockaddr_in sin; // Властивості(адреса) сокету, що створюється

SOCKET Sock, Client; // Серверний и клієнтський сокети

WSAStartup(0x0202,&WSADat); // Ініціалізація WinSock

// 0x0202 - версія WinSock. Може бути 1.0, 1.1, 2.0, 2.2

// WSADat - структура, куди будуть занесені рез. ініціалізації

gethostname(PCName, 30); // Отримання імені поточного ПК

sin.sin_family = AF_INET; // Тип адреси

sin.sin_addr.s_addr = 0; // IP-адреса сервера (при створенні сервера 0)

sin.sin_port = htons(2803); // Номер порту сервера

Sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); // Створення сокету

bind(Sock, (sockaddr*)&sin, sizeof(sin)); // Зв’язування створеного сокету з адресою sin

// ***** Очікування клієнта

cout << "Wait of client..." << endl;

listen(Sock, SOMAXCONN); // Прослуховування сокету сервером (для підключення клієнта)

Client = accept(Sock, (sockaddr*)&sin, 0); // Очікування клієнту

recv(Client, ClientName, 30, 0); // Отримання імені компьютера клієнту

send(Client, PCName, strlen(PCName) + 1, 0); // Відправлення імені компьютеру (серверу)

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

if (recv(Client, Message, 200, 0) != SOCKET_ERROR) {

cout << "Massiv A" << endl;

for (i=0;i<4;i++){

cout<<Message[i]<<"\t";

if (i==1) cout <<endl;

}cout<<endl;

cout << "Massiv B" << endl;

for (i=4;i<8;i++){

cout<<Message[i]<<"\t";

if (i==5) cout <<endl;

}cout<<endl;

porivn(Message);

perestX(Message);

porivn(Message);

perestY(Message);

porivn(Message);

cin.get();

}

if (flag1==1){

if (send(Client, ne, strlen(ne) + 1, 0) != SOCKET_ERROR) cout << "Sent!\n";

cin.get();

}

else {

if (send(Client, nr, strlen(nr) + 1, 0) != SOCKET_ERROR) cout << "Sent!\n";

else {cout << "Error of sending!\n";

cin.get();

}

}

// Закриття сокетів і завершення роботи з WinSock

closesocket(Sock);

closesocket(Client);

WSACleanup();

}