Помощничек
Главная | Обратная связь


Археология
Архитектура
Астрономия
Аудит
Биология
Ботаника
Бухгалтерский учёт
Войное дело
Генетика
География
Геология
Дизайн
Искусство
История
Кино
Кулинария
Культура
Литература
Математика
Медицина
Металлургия
Мифология
Музыка
Психология
Религия
Спорт
Строительство
Техника
Транспорт
Туризм
Усадьба
Физика
Фотография
Химия
Экология
Электричество
Электроника
Энергетика

Сравнение последовательных и параллельных реализаций.

Лабораторная работа №3

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

Цель работы ­–изучить методы создания серверов, используя алгоритм последовательной обработки запросов.

 

 

Особенности параллельного соединения

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

Рис. 3.1. Схема организации процессов параллельного сервера с установлением логического соединения, в котором используются однопотоковые процессы

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

 

Сравнение последовательных и параллельных реализаций.

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

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

 

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

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

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

API-интерфейс потоков предоставляет функции, которые могут использоваться потоками для координации работы. Однако многие библиотечные функции, возвращающие указатели на статические элементы данных, не являются безопасными с точки зрения потоков, а это означает, что результаты вызова таких функций могут оказаться непредсказуемыми. Например, рассмотрим библиотечную функцию gethostbyname(), используемую в приложениях для преобразования доменного имени в IP-адрес. Если два потока вызовут функцию gethostbyname() одновременно, то ответ на один поисковый запрос мажет быть перекрыт ответом на другой. Поэтому если несколько потоков вызывают определенную библиотечную функцию, они должны координировать свою работу для обеспечения того, чтобы в любое время ее вызов выполнял только один поток.

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

 

Методические указания

Рассмотрим многопоточность на следующем примере.

Осуществить взаимодействие клиента и сервера на основе протокола TCP/IP. Реализовать параллельное соединение с использованием многопоточности. Функциональные возможности клиента реализовать следующим образом: клиент вводит с клавиатуры строку символов и посылает ее серверу. Признак окончания ввода строки – нажатие клавиши "Ввод". Функциональные возможности сервера реализовать следующим образом: сервер, получив эту строку, должен определить длину введенной строки, и, если эта длина кратна 4, то первая часть строки меняется местами со второй. Результаты преобразований возвращаются назад клиенту.

Как и в предыдущих лабораторных работах будем использовать разработку приложений типа клиент/сервер.

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

#include<stdio.h>

#include<iostream.h>

#include <winsock2.h>

 

Функция CreateThread( ) создает поток, который выполняться в пределах адресного пространства вызова процесса и имеет следующий прототип:

 

HANDLE Create Thread (

LPSECURITY_ATTRIBUTES lpThreadAttributes, // указатель на атрибуты

безопасности

DWORD dwStackSize, // размер стека начального потока

LPTHREAD_START_ROUTINE lpStartAddress, // указатель на функцию

потока

LPVOID lpParameter, // аргумент для нового потокаDWORD dwCreationFlags,// создание флагов

LPDWORD lpThreadId// указатель на ID поток для его получения

);

 

Рассмотрим каждый параметр подробнее:

 

lpThreadAttributes --представляет собой указатель на структуру SECURITY_ATTRIBUTES, который определяет, может ли дескриптор потока быть унаследован дочерними процессами. Если lpThreadAttributes принимает значение NULL, дескриптор потока не может быть унаследован.

Для Windows NT: член структуры lpSecurityDescriptor определяет дескриптор безопасности для нового потока. Если lpThreadAttributes принимает значение NULL, то поток получает дескриптор безопасности по умолчанию.

 

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

 

lpStartAddress –указатель на определенную прикладную функцию типа LPTHREAD_START_ROUTINE, для выполнения ее потоком и представления начального адреса потока.

 

lpParameter –определяет единственную 32- битовую величину параметра, переданную в поток.

dwCreationFlags –используется для определения дополнительных флагов, которые управляют созданием потока. Если флаг CREATE_SUSPENDED определен, поток создан в приостановленном состоянии и не будет работать, пока функция ResumeThread( ) не будет вызвана. Если эта нулевая величина, то поток выполняется немедленно после его создания. В то же время, никакие другие величины не предусмотрены.

lpThreadId –указатель на 32- битовую переменную, которая получает идентификатор потока.

ДляWindows NT: Если этот параметр принимает значение NULL, то идентификатор потока не возвращается.

Для Windows 95 и Windows 98: Этот параметр может не принимать значение NULL.

Возвращаемые значения:

Если функция CreateThread( ) успешно выполняется, то возвращаемое значение есть указатель на новый поток. В случае невыполнения функции, возвращаемое значение принимает значение NULL. Для получения большей информации об ошибке, обратимся к функции GetLastError( ).

Примечание:

 

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

ü Выполнение потока начинается в функции определенной параметром lpStartAddress. Если эта функция возвращает значение типа DWORD, то оно используется для завершения потока неявным вызовом функции ExitThread( ),которая будет описана ниже. Используйте функцию GetExitCodeThread( ), чтобы получать возвращаемое значение потока.

ü Функция CreateThread( ) выполняется, даже если указатель lpStartAddress указывает на данные, код, или не доступен. Если начальный адрес недействителен во время работы потока, срабатывает исключение и поток завершается. Завершение потока из-за неправильного начального адреса интерпретируется как аварийный выход процесса потока.

 

В нашей программе основной алгоритм решения задачи находится в функции ThreadFunc( ), которую мы определим сами следующим образом:

DWORD WINAPI ThreadFunc(LPVOID client_socket)

{

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

char buf[100];

char buf1[100];

//send(s2,"Welcome new client!\n",sizeof("Welcome new client!\n"),0);

while(recv(s2,buf,sizeof(buf),0))

{

int k, j=0;

k=strlen(buf)-1;

 

if(k%4==0)

{

for(int i=k/2; i<k; i++)

{

buf1[i]=buf[j];

j++;

}

 

for(i=0; i<k/2; i++)

{

buf1[i]=buf[j];

j++;

}

 

buf1[k]='\0';

strcpy(buf,buf1);

}

cout<<buf<<endl;

send(s2,buf,100,0);

}

closesocket(s2);

return 0;

}

При вызове функции ThreadFunc( ) в основной программе (main-функции) передается дескриптор сокета.

int numcl=0;

 

void print()

{

if (numcl) printf("%d client connected\n",numcl);

else printf("No clients connected\n");

}

 

void main()

{

WORD wVersionRequested;

WSADATA wsaData;

int err;

wVersionRequested = MAKEWORD( 2, 2 );

err = WSAStartup( wVersionRequested, &wsaData );

if ( err != 0 ){return;}

 

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));

int c=listen(s,5);

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

cout<<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)))

{

numcl++;

print();

// Вызов нового потока для обслуживания клиента

DWORD thID;// thID идентификатор типа DWORD

CreateThread(NULL,NULL,ThreadFunc,&client_socket,NULL,&thID)

}

}

По окончанию работы функция CreateThread( ) закрывает поток, инициализирует используемые указатели значением NULL. Существует специальная функция Exit Thread( ), выполняющая аналогичные действия. Ее прототип:

 

VOID ExitThread( DWORD dwExitCode );

 

Параметр dwExitCodeопределяет выходной код для вызова потока. Данная функция не возвращает никакого значения.

 

Примечание:

ü Когда функция ExitThread( ) явно вызвана, текущий стек потока освобожден и поток завершается.

ü Если поток является последним в процессе, когда эта функция вызвана, то процесс потока также завершается.

ü Завершение потока не обязательно удаляет его объект из операционной системы. Объект потока удален, когда закрывается последний дескриптор потока.

Клиентская часть во многом дублирует клиент-приложения предыдущих лабораторных работ. Согласно требованиям условия задачи клиентская часть имеет следующий вид:

 

#include<stdio.h>

#include<iostream.h>

#include<winsock2.h>

 

void main()

{

WORD wVersionRequested;

WSADATA wsaData;

int err;

wVersionRequested = MAKEWORD( 2, 2 );

err=WSAStartup( wVersionRequested, &wsaData );

if ( err != 0 ){return;}

 

while (true)

{

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));

 

char buf[100];

 

cout<<"Enter the string:"<<endl;

fgets(buf,sizeof(buf),stdin);

send(s,buf,100,0);

 

if (recv(s,buf,sizeof(buf),0)!=0)

{

cout<<"Poluchenaya stroka:"<<endl<<buf<<endl;

}

closesocket(s);

}

WSACleanup();

}

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

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

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

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

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

5. Что позволяет контролирующему потоку формировать отчеты об активности ведомых потоков сервера для системного администратора?

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

7. Какие функции служат для создания потоков?

 

Варианты индивидуального задания

1. Осуществить взаимодействие клиента и сервера на основе протокола TCP/IP. Реализовать параллельное соединение с использованием многопоточности. Функциональные возможности клиента реализовать следующим образом: клиент вводит с клавиатуры строку символов и посылает ее серверу. Признак окончания ввода строки – нажатие клавиши "Ввод". Функциональные возможности сервера реализовать следующим образом: сервер, получив эту строку, должен определить длину введенной строки, и, если эта длина кратна 3, то удаляются все числа, которые делятся на 3. Результаты преобразований этой строки возвращаются назад клиенту.

2. Осуществить взаимодействие клиента и сервера на основе протокола TCP/IP. Реализовать параллельное соединение с использованием многопоточности. Функциональные возможности клиента реализовать следующим образом: клиент вводит с клавиатуры строку символов и посылает ее серверу. Признак окончания ввода строки – нажатие клавиши "Ввод". Функциональные возможности сервера реализовать следующим образом: сервер, получив эту строку, должен определить длину введенной строки, и, если эта длина четная, то удаляются 3 первых и 2 последних символа. Результаты преобразований этой строки возвращаются назад клиенту.

3. Осуществить взаимодействие клиента и сервера на основе протокола TCP/IP. Реализовать параллельное соединение с использованием многопоточности. Функциональные возможности клиента реализовать следующим образом: клиент вводит с клавиатуры строку символов и посылает ее серверу. Функциональные возможности сервера реализовать следующим образом: сервер, получив эту строку, должен выяснить, имеются ли среди символов этой строки все буквы, входящие в слово WINDOWS. Количество вхождений символов в строку передать назад клиенту.

4. Осуществить взаимодействие клиента и сервера на основе протокола TCP/IP. Реализовать параллельное соединение с использованием многопоточности. Функциональные возможности клиента реализовать следующим образом: клиент вводит с клавиатуры строку символов и посылает ее серверу. Признак окончания ввода строки – нажатие клавиши "Ввод". Функциональные возможности сервера реализовать следующим образом: сервер, получив эту строку, должен определить длину введенной строки, и, если эта длина нечетная, то удаляется символ, стоящий посередине строки. Преобразованная строка передается назад клиенту.

5. Осуществить взаимодействие клиента и сервера на основе протокола TCP/IP. Реализовать параллельное соединение с использованием многопоточности. Функциональные возможности клиента реализовать следующим образом: клиент вводит с клавиатуры строку символов и посылает ее серверу. Признак окончания ввода строки – нажатие клавиши "Ввод". Функциональные возможности сервера реализовать следующим образом: сервер, получив эту строку, должен заменить в этой строке каждый второй символ @ на #. Результаты преобразований передаются назад клиенту.

6. Осуществить взаимодействие клиента и сервера на основе протокола TCP/IP. Реализовать параллельное соединение с использованием многопоточности. Функциональные возможности клиента реализовать следующим образом: клиент вводит с клавиатуры строку символов и посылает ее серверу. Признак окончания ввода строки – нажатие клавиши "Ввод". Функциональные возможности сервера реализовать следующим образом: сервер, получив эту строку, должен заменить в этой строке символов все пробелы на символ *. Преобразованная строка передается назад клиенту.

7. Осуществить взаимодействие клиента и сервера на основе протокола TCP/IP. Реализовать параллельное соединение с использованием многопоточности. Функциональные возможности клиента реализовать следующим образом: клиент вводит с клавиатуры строку символов и посылает ее серверу. Признак окончания ввода строки – нажатие клавиши "Ввод". Функциональные возможности сервера реализовать следующим образом: сервер, получив эту строку, должен определить длину введенной строки, и, если длина больше 15, то из нее удаляются все цифры. Клиент получает преобразованную строку и количество удаленных цифр.

8. Осуществить взаимодействие клиента и сервера на основе протокола TCP/IP. Реализовать параллельное соединение с использованием многопоточности. Функциональные возможности клиента реализовать следующим образом: клиент вводит с клавиатуры строку символов и посылает ее серверу. Признак окончания ввода строки – нажатие клавиши "Ввод". Функциональные возможности сервера реализовать следующим образом: сервер, получив эту строку, должен определить длину введенной строки, и, если длина кратна 4, то удаляются все числа, делящиеся на 4. Клиент получает преобразованную строку и количество таких чисел.

9. Осуществить взаимодействие клиента и сервера на основе протокола TCP/IP. Реализовать параллельное соединение с использованием многопоточности. Функциональные возможности клиента реализовать следующим образом: клиент вводит с клавиатуры строку символов и посылает ее серверу. Признак окончания ввода строки – нажатие клавиши "Ввод". Функциональные возможности сервера реализовать следующим образом: сервер, получив эту строку, должен определить длину введенной строки, и, если эта длина кратна 5, то подсчитывается количество скобок всех видов. Их количество посылается клиенту.

10. Осуществить взаимодействие клиента и сервера на основе протокола TCP/IP. Реализовать параллельное соединение с использованием многопоточности. Функциональные возможности клиента реализовать следующим образом: клиент вводит с клавиатуры строку символов и посылает ее серверу. Признак окончания ввода строки – нажатие клавиши "Ввод". Функциональные возможности сервера реализовать следующим образом: сервер, получив эту строку, должен определить длину введенной строки, и, если эта длина кратна 4, то первая часть строки меняется местами со второй. Результаты преобразований возвращаются назад клиенту.

11. Осуществить взаимодействие клиента и сервера на основе протокола TCP/IP. Реализовать параллельное соединение с использованием многопоточности. Функциональные возможности клиента реализовать следующим образом: клиент вводит с клавиатуры строку символов и посылает ее серверу. Признак окончания ввода строки – нажатие клавиши "Ввод". Функциональные возможности сервера реализовать следующим образом: сервер, получив эту строку, должен определить длину введенной строки, и, если значение этой длины равно 10, то удаляются все символы от A до Z. Результаты преобразований такой строки и количество удалений возвращаются назад клиенту.

12. Осуществить взаимодействие клиента и сервера на основе протокола TCP/IP. Реализовать параллельное соединение с использованием многопоточности. Функциональные возможности клиента реализовать следующим образом: клиент вводит с клавиатуры строку символов и посылает ее серверу. Признак окончания ввода строки – нажатие клавиши "Ввод". Функциональные возможности сервера реализовать следующим образом: сервер, получив эту строку, должен определить длину введенной строки, и, если длина больше 15, то удаляются все символы от a до z. Преобразованная строка и количество удаленных символов возвращаются назад клиенту.

13. Осуществить взаимодействие клиента и сервера на основе протокола TCP/IP. Реализовать параллельное соединение с использованием многопоточности. Функциональные возможности клиента реализовать следующим образом: клиент вводит с клавиатуры строку символов и посылает ее серверу. Признак окончания ввода строки – нажатие клавиши "Ввод". Функциональные возможности сервера реализовать следующим образом: сервер, получив эту строку, должен в полученной строке символов поменять местами символы на четных и нечетных позициях. Полученную строку возвратить назад клиенту.

14. Осуществить взаимодействие клиента и сервера на основе протокола TCP/IP. Реализовать параллельное соединение с использованием многопоточности. Функциональные возможности клиента реализовать следующим образом: клиент вводит с клавиатуры строку символов и посылает ее серверу. Признак окончания ввода строки – нажатие клавиши "Ввод". Функциональные возможности сервера реализовать следующим образом: сервер, получив эту строку, должен определить длину введенной строки, и, если длина больше 7, то выделяется подстрока в { } скобках и возвращается назад клиенту.

15. Осуществить взаимодействие клиента и сервера на основе протокола TCP/IP. Реализовать параллельное соединение с использованием многопоточности. Функциональные возможности клиента реализовать следующим образом: клиент вводит с клавиатуры строку символов и посылает ее серверу. Признак окончания ввода строки – нажатие клавиши "Ввод". Функциональные возможности сервера реализовать следующим образом: сервер, получив эту строку, должен определить длину введенной строки и, если длина больше 15, то выделяется подстрока до первого пробела и возвращается назад клиенту.

 

 




Поиск по сайту:

©2015-2020 studopedya.ru Все права принадлежат авторам размещенных материалов.