Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
ИПР№2.docx
Скачиваний:
15
Добавлен:
19.01.2020
Размер:
218.24 Кб
Скачать

Министерство образования Республики Беларусь

Учреждение образования

«Белорусский государственный университет информатики и

радиоэлектроники»

Кафедра экономической информатики

ИНДИВИДУАЛЬНАЯ ПРАКТИЧЕСКАЯ РАБОТА №2

по дисциплине: «Компьютерные сети»

на тему: «Создание параллельного многопоточного сервера с установлением логического соединения (TCP)»

Вариант 1

Выполнила студентка: Быкович Е.И.

Группа 792351

Минск 2020

Индивидуальное задание

Разработать приложение, реализующее архитектуру «клиент-сервер». Необходимо реализовать параллельный многопоточный сервер с установлением логического соединения (TCP). Логику взаимодействия клиента и сервера реализовать следующим образом: На сервере хранится список о студентах. Каждая запись списка содержит следующую информацию о студенте:

- ФИО студента;

- номер группы;

- размер стипендии;

- оценки по № предметам.

Таких записей должно быть не менее 5.

Клиент вводит с клавиатуры букву алфавита, по которой он хотел бы посмотреть информацию о студентах и посылает ее на сервер. Назад он получает список только тех студентов, фамилии которых начинаются на эту букву.

Нам необходимо написать две программы серверную и клиентскую.

В серверной программе инициализируем WinSock API, используя функцию WSAStartup. Создаем сокет, использующий протокол TCP, при помощи функции socket. Устанавливаем сокету адрес и порт, используя функцию bind. Далее ожидаем установки соединения с клиентом, для чего используем функцию listen. После прихода с клиента предложения о соединении, принимаем его, используя функцию accept. И начинаем принимать данные с клиента, используя функцию recv. Принятые данные обрабатываем, исходя из условия задачи, и отсылаем результат обработки, используя функцию send. В конце программы закрываем сокет, при помощи функции closesocket, и прекращаем работу WinSock API, используя функцию WSACleanup.

В клиентской программе инициализируем WinSock API, используя функцию WSAStartup. Создаем сокет, использующий протокол TCP, при помощи функции socket. Устанавливаем соединение с сервером, используя функцию connect. Считываем данные с потока входа, при помощи функции std::cin.getline, и отсылаем их на сервер, используя функцию send. Получаем результат с сервера, используя функцию recv, и выводим его на экран, посредством функции std::cout. В конце программы закрываем сокет, при помощи функции closesocket, и прекращаем работу WinSock API, используя функцию WSACleanup.

Серверная часть:

//#define WIN32_LEAN_AND_MEAN

#include <iostream>

#include <WinSock2.h>

#include <cstring>

#include <string>

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

#pragma warning(disable: 4996)

using namespace std;

struct teachers

{

string name;

int group;

double scholarship;

double marks;

};

teachers table[5];

DWORD WINAPI ThreadFunc(LPVOID client_socket) // функция для каждого потока

{

SOCKET soc = ((SOCKET*)client_socket)[0];

char str[10];

char sendstr[10];

recv(soc, str, 10, 0); // принимает строку

string users_subj = "";

int k = 0;

while( str[k] != '\0' ) { // переносит её в другую строку, чтобы убрать ненужные символы

users_subj.push_back(str[k]);

k++;

}

cout << users_subj;

for (int i = 0; i < 5; i++) { // цикл по всем студентам

if (table[i].name[0] == users_subj[0]) // проверяет первую букву

{

table[i].name.copy(sendstr, table[i].name.size() + 1);

sendstr[table[i].name.size()] = '\0';

send(soc, sendstr, sizeof(sendstr), 0); // посылает на сервер

}

}

send(soc, "7", sizeof("7"), 0); // посылает код, что передача закончилась

closesocket(soc);

return 0;

}

int main() {

table[0].scholarship = table[1].scholarship = table[2].scholarship = table[3].scholarship

= table[4].scholarship = 70.2;

table[0].name = "Petrov"; table[1].name = "Ivanov"; table[2].name = "Ilin";

table[3].name = "Selby"; table[4].name = "Poleshiuk";

table[0].group = table[2].group = table[4].group = 620406;

table[1].group = table[3].group = 720503;

for (int i = 0; i < 5; i++)

{

table[i].marks = i + 3;

cout << table[i].name << endl << "Subject " << table[i].group << endl << "Experience: "

<< table[i].marks << endl << table[i].scholarship << endl << endl;

}

WORD wVersionRequested = MAKEWORD(2, 2);

WSADATA wsaData;

if (WSAStartup(wVersionRequested, &wsaData) != 0)

return 0;

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

sockaddr_in local_addr;

local_addr.sin_family = AF_INET;

local_addr.sin_port = htons(1280);

local_addr.sin_addr.s_addr = 0;

bind(s, (sockaddr*)&local_addr, sizeof(local_addr));

listen(s, 5);

cout << "Server receive ready" << endl << endl;

SOCKET client_socket;

sockaddr_in client_addr;

int client_addr_size = sizeof(client_addr);

while (client_socket = accept(s, (sockaddr*)&client_addr, &client_addr_size)) //принимает запрос

{

DWORD thID;

CreateThread(NULL, NULL, ThreadFunc, &client_socket, NULL, &thID); // создаёт новый поток

}

return 0;

}

Клиентская часть:

#include <iostream>

#include <WinSock2.h>

#include "conio.h"

#include <cstring>

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

#pragma warning(disable: 4996)

using namespace std;

int main()

{

WORD wVersionRequested = MAKEWORD(2, 2);

WSADATA wsaData;

if (WSAStartup(wVersionRequested, &wsaData) != 0)

return 0;

bool flag = true;

while (flag) // бесконечный цикл для создания новых запросов

{

SOCKET s = socket(AF_INET, SOCK_STREAM, 0); // создание сокета

sockaddr_in dest_addr;

dest_addr.sin_family = AF_INET;

dest_addr.sin_port = htons(1280);

dest_addr.sin_addr.s_addr = inet_addr("127.0.0.1");

connect(s, (sockaddr*)&dest_addr, sizeof(dest_addr)); // подключение к серверу

cout << "Enter letter" << endl;

char name[10];

cin >> name;

send(s, name, sizeof(name), 0); // посылает букву

char accepted_string[10];

cout << "Students with name starting on this letter:" << endl;

while(true) {

recv(s, accepted_string, sizeof(accepted_string), 0); // принимает в цикле ответ сервера

if (accepted_string[0] == '7') break; // проверяет, не закончилась ли передача

cout << accepted_string << endl;

}

closesocket(s); // закрывает сокет

cout << "Would u like to continue? (Y/N)" << endl;

while (true){

char sym = _getch();

if (sym == 'Y' || sym == 'y')

break;

else

if (sym == 'N' || sym == 'n')

{

flag = false; // выход из цикла

break;

}

}

}

WSACleanup();

return 0;

}

Контрольные вопросы

  1. Что такое параллельное соединение? Особенности параллельного соединения.

Мнoгoпoтoчнoсть — этo специализирoвaнная форма мнoгoзaдaчнoсти (multitasking). Что касается многозадачности, то выделяют двa типa мнoгoзaдaчнoсти: oснoвaнную нa прoцессaх (process-based) и oснoвaнную нa пoтoкaх (thread-based). Пo сути, прoцесс (process) — этo выпoлняющaяся прoгрaммa. Тaким oбрaзoм, oснoвaннaя нa прoцессaх мнoгoзaдaчнoсть — средствo, пoзвoляющее вaшему кoмпьютеру выпoлнять нескoлькo прoгрaмм oднoвременнo. Отличия oснoвaннoй нa прoцессaх и мнoгoпoтoчнoй мнoгoзaдaчнoсти мoжнo сфoрмулирoвaть следующим oбрaзoм: первaя пoддерживaет oднoвременнoе выпoлнение нескoльких прoгрaмм, a втoрaя имеет делo с oднoвременным выпoлнением рaзных фрaгментoв oднoй и тoй же прoгрaммы. С помощью процессов можно организовать параллельное выполнение программ. Для этого процессы клонируются с помощью вызовов fork() или exec(), а затем между ними организуется взаимодействие средствами IPC. Это довольно дорогостоящий с точки зрения ресурсов процесс. С другой стороны, для организации параллельного выполнения и взаимодействия можно использовать механизм многопоточности. Основной единицей здесь является поток. Рассмотрим этот механизм подробнее.

Потоки Последовательная реализация сервера, о которой речь шла в предыдущих лабораторных работах, может оказаться неудовлетворительной, поскольку клиенты будут вынуждены ждать завершения обработки всех предыдущих запросов на установление соединения. Если клиент решит передать большие объемы данных (например, несколько мегабайт), последовательный сервер отложит обслуживание всех других клиентов до тех пор, пока не выполнит этот запрос. Параллельная реализация сервера дает возможность обойтись без продолжительных задержек, так как не позволяет одному клиенту захватить все ресурсы. Вместо этого параллельный сервер поддерживает обмен данными сразу с несколькими клиентами для того, чтобы их запросы выполнялись одновременно.

Поэтому, с точки зрения клиента, параллельный сервер обеспечивает лучшее наблюдаемое время отклика по сравнению с последовательным сервером. Распараллеливание сервера достигается созданием отдельного потока для обработки запросов одного клиента или отдельного однопотокового процесса для обработки запросов одного клиента.

  1. Отличие параллельного соединения от последовательного?

Последовательная реализация сервера может оказаться неудовлетворительной, поскольку клиенты будут вынуждены ждать завершения обработки всех предыдущих запросов на установление соединения. Если клиент решит передать большие объемы данных (например, несколько мегабайт), последовательный сервер отложит обслуживание всех других клиентов до тех пор, пока не выполнит этот запрос.

Параллельная реализация сервера дает возможность обойтись без продолжительных задержек, т.к. не позволяет одному клиенту захватить все ресурсы. Вместо этого параллельный сервер поддерживает обмен данными сразу с несколькими клиентами для того, чтобы их запросы выполнялись одновременно. Поэтому, с точки зрения клиента, параллельный сервер обеспечивает лучшее наблюдаемое время отклика по сравнению с последовательным сервером.

  1. Назовите преимущества многопотоковых процессов по сравнению с однопотоковыми процессами.

Многопотоковые процессы обладают двумя основными преимуществами по сравнению с однопотоковыми процессами: более высокая эффективность и разделяемая память. Повышение эффективности связано с уменьшением издержек на переключение контакта. Переключателем контакта называются действия, выполняемые операционной системой при передаче ресурсов процессора то одного потока выполнения к другому. При переключении с одного потока на другой операционная система должна сохранить в памяти состояние предыдущего потока (например, значение регистров) и восстановить состояние следующего потока. Потоки в одном и том же процессе разделяют значительную часть информации о состоянии процесса, поэтому операционной системе приходится выполнять меньший объем работы по сохранению и восстановлению состояния. Вследствие этого переключение с одного потока на другой в одном и том же процессе происходит быстрее по сравнению с переключением между двумя потоками в разных процессах. В частности, поскольку потоки одного и того е прочеса разделяют адресное пространство памяти, то переключение между потоками процесса означает, что операционная система не должна менять отображение виртуальной памяти на физическую. Второе преимущество потоков, т.е. разделяемая память, вероятно, является для программистов еще более важным, чем повышение эффективности. Потоки упрощают разработку параллельных серверов, в которых все копии сервера должны взаимодействовать друг с другом или обращаться к разделяемым элементам данных. Кроме того, потоки упрощают разработку систем контроля и управления. В частности, поскольку ведомые потоки в сервере совместно используют память, они могут записывать в глобальную память статистическую информацию, что позволяет контролирующему потоку формировать отчеты об активности ведомых потоков сервера для системного администратора.

  1. С чем связано повышение эффективности многопотоковых процессов?

Если операционная система поддерживает концепции потоков в рамках одного процесса, она называется многопоточной. Многопоточные приложения имеют ряд преимуществ:

    • улучшенная реакция приложения - любая программа, содержащая много не зависящих друг от друга действий, может быть перепроектирована так, чтобы каждое действие выполнялось в отдельном потоке. Например, пользователь многопоточного интерфейса не должен ждать завершения одной задачи, чтобы начать выполнение другой;

    • более эффективное использование мультипроцессирования - как правило, приложения, реализующие параллелизм через потоки, не должны учитывать число доступных процессоров. Производительность приложения равномерно увеличивается при наличии дополнительных процессоров. Численные алгоритмы и приложения с высокой степенью параллелизма, например перемножение матриц, могут выполняться намного быстрее;

    • улучшенная структура программы - некоторые программы более эффективно представляются в виде нескольких независимых или полуавтономных единиц, чем в виде единой монолитной программы. Многопоточные программы легче адаптировать к изменениям требований пользователя;

    • эффективное использование ресурсов системы - программы, использующие два или более процессов, которые имеют доступ к общим данным через разделяемую память, содержат более одного потока управления. При этом каждый процесс имеет полное адресное пространство и состояние в операционной системе. Стоимость создания и поддержания большого количества служебной информации делает каждый процесс более затратным, чем поток. Кроме того, разделение работы между процессами может потребовать от программиста значительных усилий, чтобы обеспечить связь между потоками в различных процессах или синхронизировать их действия.

Соседние файлы в предмете Компьютерные сети