ВУЗ: Не указан

Категория: Курсовая работа

Дисциплина: Не указана

Добавлен: 22.04.2023

Просмотров: 352

Скачиваний: 7

ВНИМАНИЕ! Если данный файл нарушает Ваши авторские права, то обязательно сообщите нам.

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

2.3 Разделение клиент-серверных приложений на уровни

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

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

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

В качестве примера выделения уровня обработки в клиент-серверном приложении можно рассмотреть поисковую машину сети Интернет.

Интерфейс поисковой системы достаточно прост: пользователь вводит строку, состоящую из ключевых слов, и получает страницу результатов поиска со списком заголовков веб-страниц. Результат формируется из огромной базы просмотренных и проиндексированных страниц. Ядром поисковой машины является программа, трансформирующая введенную пользователем строку в один или несколько запросов к базе данных. Затем система помещает результаты запроса в список и преобразует этот список в набор веб-страниц. В рамках модели клиент-сервер часть, которая отвечает за выборку информации, обычно находится на уровне обработки.


3. Уровень данных. Уровень в модели клиент-сервер обычно содержит собственно данные, с которыми происходит работа, а точнее приложения, обрабатывающие эти данные.

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

Пользовательский интерфейс

УРОВЕНЬ ПОЛЬЗОВАТЕЛЬСКОГО ИНТЕРФЕЙСА

Выражение с ключевыми словами (поисковый запрос пользователя)

HTML-страница результатов поиска поисковой системы (список заголовков сайтов)

УРОВЕНЬ ОБРАБОТКИ ДАННЫХ

Генератор (фильтр)

запросов к базе данных

Генератор HTML-страниц

Упорядоченный список заголовков страниц

Запросы к базе данных

Компонент упорядочивания (ранжирования) списков веб-страниц

Заголовки веб-страниц с метаинформацией

База данных

проиндексированных веб-страниц

поисковой системы

УРОВЕНЬ ДАННЫХ

Рисунок 10 – Трехуровневая организация клиент-серверного приложения на примере поисковой системы

В модели клиент-сервер уровень данных обычно находится на стороне сервера. Кроме простого хранения данных уровень обычно отвечает за поддержание целостности хранимых данных для различных приложений. Уровень данных зачастую организуется в форме реляционной базы данных. Ключевым здесь является независимость данных. Данные организуются независимо от приложений так, чтобы изменения в организации данных не влияли на приложения, а приложения не оказывали влияния на организацию данных. Использование реляционных баз данных в модели клиент-сервер помогает отделить уровень обработки от уровня данных, но существует обширный класс приложений, для которых реляционные базы данных не являются лучшим выбором. Примерами таких приложений могут служить те, которые работают со сложными типами данных, т.е. данные которые проще моделировать в понятиях объектов, а не отношений. Примеры сложных типов могут служить простые наборы прямоугольников и проекты самолетов в случае систем автоматизированного проектирования. Мультимедиа системам значительно проще работать с видео- и аудиопотоками, используя специфичные для них операции, чем с моделями этих потоков в виде реляционных таблиц. В случае, когда операции с данными проще выразить в понятиях работы с объектами, имеет смысл реализовать уровень данных средствами объектно-ориентированных баз данных. Таким образом, часть функциональных возможностей, которые приходились на уровень обработки переходят в таком случае на уровень данных [17].


3. Программная модель клиента и сервера

3.1 Исходный код определений клиента и сервера

В качестве упрощенного примера рассмотрим описание клиента и файлового сервера на языке Си. Клиент и сервер должны совместно использовать определения, включаемые в тексты программ клиента и сервера директивой #include <header.h>, которые собраны в файле под названием header.h [4], [5].

Файл header.h, используемый клиентом и сервером.

Рисунок 11 – Упрощенный исходный код файла определений header.h

Константы МАХ_РАТН и BUF_SIZE определяют размер двух массивов, используемых в сообщении. Константа MAX_PATH определяет число символов, которое может содержаться в имени файла, т.е. в строке с путем типа /home/user/documents/kursovaya.t. Константа BUF_SIZE задает размер блока данных, который может быть прочитан или записан за одну операцию путем установки размера буфера.

Константа FILE_SERVER задает сетевой адрес файлового сервера, на который клиенты могут посылать сообщения.

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

Важная часть – определения формата сообщения. В примере это структура с восемью полями. Формат используется во всех запросах клиентов к серверу и ответах сервера клиенту. Поля source и dest определяют отправителя и получателя. Поле opcode – одна из ранее определенных операций: создание, чтение, запись или удаление. Поля count и offset служат для передачи параметров. Поле result в запросах от клиента к серверу не используется, а при ответах сервера клиенту содержит значение результата. В конце структуры два массива. Массив name содержит имя файла к которому клиент обращается. Массив data содержит данные, возвращаемые сервером при чтении или передаваемые на сервер при записи.


3.2 Исходный код сервера

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

/* курсовая работа: клиент-сервер */

/* код сервера */

#include <header.h>

void main(void) {

struct message m1, m2; /* входящее и исходящее сообщения */

int r; /* код результата */

while (TRUE) { /* сервер работает непрерывно */

receive(FILE_SERVER, &m1); /* блок ожидания сообщения */

switch(mi.opcode) { /* в зависимости от типа запроса */

case CREATE: r = do_create(&m1, &m2); break;

case READ: r = do_read(&m1, &m2); break;

case WRITE: r = do_write(&m1, &m2); break;

case DELETE: r = do_delete(&m1, &m2); break;

default: r = E_BAD_OPER;

}

m2.result = r; /* вернуть результат клиенту */

send(mi.source, &m2); /* послать ответ */

}

}

Рисунок 12 – Упрощенный исходный код файлового сервера

3.3 Исходный код клиента

Программа клиента реализует копирование файлов с использованием сервера. Тело процедуры содержит цикл чтения блока из исходного файла и записи его в файл-приемник. Цикл повторяется до тех пор, пока исходный файл не будет полностью скопирован, что определяется по коду возврата операции чтения – должен быть ноль или отрицательное число [17].

Первая часть цикла состоит из создания сообщения для операции чтения и пересылки его на сервер.

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

/* курсовая работа: клиент-сервер */

/* код клиента */

#include <header.h>

/* процедура копирования файла через сервер */


int copy(char *src, char *dst) {

struct message m1; /* буфер сообщения */

long position; /* текущая позиция в файле */

long client = 110; /* адрес клиента */

initialize(); /* инициализация, подготовка к выполнению */

position = 0; /* инициализация начального значения позиции */

/* начало цикла с постусловием (тело цикла выполняется минимум один раз) */

do {

m1.opcode = READ; /* операция чтения */

m1.offset = position; /* текущая позиция в файле */

m1.count = BUF_SIZE; /* сколько байт прочитать */

strcpy(&m1, name, src); /* скопировать имя читаемого файла */

send(FILE_SERVER, &m1); /* послать сообщение на файловый сервер */

receive(client, &m1); /* блок ожидания ответа */

m1.opcode = WRITE; /* записать полученные данные в файл-приемник, операция записи */

m1.offset = position; /* текущая позиция в файле */

m1.count = m1.result; /* сколько байт записать */

strcpy(&m1, name, clst); /* скопировать имя записываемого файла */

send(FILE_SERVER, &m1); /* послать сообщение на файловый сервер */

receive(client, &m1); /* блок ожидания ответа */

position += m1.result; /* в m1.result содержится количество записанных байт */

} while(m1.result > 0); /* повторять до окончания */

/* вернуть OK или код ошибки */

return(m1.result >= 0 ? OK : m1.result);

}

Рисунок 13 – Упрощенный исходный код клиента, который использует файловый сервер для копирования файлов

В коде отсутствуют детали, такие как отсутствие кода процедур do_xxxх, которые выполняют работу, отсутствует обработка ошибок.

ЗАКЛЮЧЕНИЕ

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