Система распределенного доступа к текстовому документу

Разработка системы распределенного доступа к текстовому документу, состоящей из сервера и клиентов, которые взаимодействуют между собой по сети. Проектирование структуры системы, протокола взаимодействия, серверной и клиентской части; тестирование.

Рубрика Программирование, компьютеры и кибернетика
Вид курсовая работа
Язык русский
Дата добавления 23.04.2014
Размер файла 1,4 M

Отправить свою хорошую работу в базу знаний просто. Используйте форму, расположенную ниже

Студенты, аспиранты, молодые ученые, использующие базу знаний в своей учебе и работе, будут вам очень благодарны.

Размещено на http://www.allbest.ru

Размещено на http://www.allbest.ru

Содержание

Введение

1. Разработка системы распределённого доступа к текстовому документу

1.1 Анализ требований

1.2 Проектирование

1.2.1 Проектирование структуры системы

1.2.2 Проектирование протокола взаимодействия

1.2.3 Проектирование серверной части

1.2.4 Проектирование клиентской части

1.3 Кодирование

2. Тестирование

Заключение

Список использованных источников

Приложения

Введение

Системное программирование -- род деятельности, заключающийся в работе над системным программным обеспечением. Основная отличительная черта системного программирования по сравнению с прикладным программированием заключается в том, что результатом последнего является выпуск программного обеспечения, предлагающего определённые услуги пользователям (например, текстовый процессор). В то время как результатом системного программирования является выпуск программного обеспечения, предлагающего сервисы по взаимодействию с операционной системой, что подразумевает сильную зависимость таких программ от операционной системы. Например, средства межпроцессного взаимодействия в ОС Windows и Unix отличаются настолько, что значительную часть работы по написанию приложений под них составляет системное программирование. В частности выделим следующие особенности системного программирования:

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

обычно используется низкоуровневый язык программирования (ассемблер) или близкий к низкому уровню (С/С++).

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

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

Целью курсового проектирования является разработка системы распределённого доступа к текстовому документу, состоящей из сервера и клиентов, которые взаимодействуют между собой по сети.

1. Разработка системы распределённого доступа к текстовому документу

1.1 Анализ требований

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

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

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

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

Разработка будет производиться на языке программирования С++ в среде разработке MS Visual Studio 2005.

1.2 Проектирование

1.2.1 Проектирование структуры системы

Система распределённого доступа к текстовым документам имеет клиент-серверную архитектуру. Работа системы осуществляется посредством запуска одного сервера и нескольких клиентов.

Перед редактированием текста, можно загрузить его из файла. По умолчанию (при старте сервера) текущий документ является пустым.

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

Общая схема системы представлена на рисунке 1.

Размещено на http://www.allbest.ru

Размещено на http://www.allbest.ru

Рисунок 1. Общая схема системы

Серверная и клиентские части системы взаимодействуют через сеть по протоколу TCP/IP. Подробно разработка протокола взаимодействия описана в разделе 1.2.2.

Как серверная, так и клиентская часть системы имеет оконный интерфейс, принятый в ОС Windows. Интерфейс серверной части позволяет загружать, посматривать и сохранять текст документа. Клиентский интерфейс выполняет задачи подключения к серверу и редактирования текста. Подробное описание серверной и клиентской частей комплекса можно найти в разделах 1.2.3 и 1.2.4.

1.2.2 Проектирование протокола взаимодействия

Для взаимодействия между клиентской и серверной частями программной системы разработан протокол взаимодействия на основе протокола TCP/IP.

Разработанный протокол взаимодействия обеспечивает:

подключение клиента к серверу;

приём от сервера исходного текста документа;

передачу на сервер транзакций с изменениями текста;

отключение клиента от сервера.

Соединение между серверной и клиентской частями осуществляется путём подключения клиента к серверному сокету. При подключении клиента, сервер передаёт ему текущую версию текста и сам текст документа.

В дальнейшем, взаимодействие осуществляется путём передачи пакетов специального формата.

Синтаксис всех видов пакетов, передаваемых между серверной и клиентской частями системы, представлен в таблице 1.

Таблица 1 - Синтаксис пакетов

Синтаксис пакета

Получатель

Пояснения

TRS[версия][старт] [удалено][добавлено][текст]

Сервер

Передача данных клиентской транзакции.

[версия] - номер версии текста, 4 байта.

[старт] - порядковый номер первого символа в тексте, затрагиваемого транзакцией, 4 байта.

[удалено] - количество удалённых из текста символов, 4 байта.

[добавлено] - количество добавленных в текст символов, 4 байта.

[текст] - добавленный текст, размер определяется полем [добавлено].

TRC[версия][старт] [удалено][добавлено][текст]

Клиент

Рассылка принятой и обработанной сервером транзакции клиентам.

[версия] - номер версии текста, 4 байта.

[старт] - порядковый номер первого символа в тексте, затрагиваемого транзакцией, 4 байта.

[удалено] - количество удалённых из текста символов, 4 байта.

[добавлено] - количество добавленных в текст символов, 4 байта.

[текст] - добавленный текст, размер определяется полем [добавлено].

YES

Клиент

Ответ-квитанция сервера об успешности транзакции, инициированной клиентом.

NOT

Клиент

Ответ-квитанция сервера об откате транзакции, инициированной клиентом.

1.2.3 Проектирование серверной части

Серверная часть системы реализована в виде диалогового Windows-приложения. Основными функциями сервера являются:

серверное приложение поддерживает загрузку текста, отображение его на экране и сохранение в файл.

сервер должен взаимодействовать одновременно с множеством клиентов.

Окно серверной части представлено на рисунке 2.

Рисунок 2. Окно сервера

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

Для каждого клиента создаётся отдельный поток ThreadProc. В этом потоке выполняется:

подключение клиента и инициализация информации о нём;

приём транзакций от клиента в цикле и реакция на них.

При приёме команды TRS, сервер получает от клиента данные о транзакции, проверяет её на актуальность (путём сравнения версий) и, в зависимости от результата, принимает или отклоняет транзакцию. При приёме транзакции изменяется текст на сервере и транзакция рассылается всем остальным клиентам (команда TRC), а клиент инициировавший транзакцию получает квитанцию YES. При отклонении транзакции клиенту передаётся квитанция об откате транзакции - NOT.

Завершается работа клиентского потока при отключении клиента от сокета.

Количество подключенных клиентов отображается в текстовой строке IDC_CLIENT_NUM в окне сервера. Обновление количества подключенных клиентов, а также текущей версии текста выполняется функцией CDistributedEditorServerDlg::UpdateInformation.

При нажатии на кнопку «Загрузить документ» (функция CDistributedEditorServerDlg::OnBnClickedLoad) открывается диалог выбора файла документа, выполняется текста из выбранного файла и вывод его на экран. Диалог выбора файла изображен на рисунке 3.

При нажатии на кнопку «Сохранить документ» (функция CDistributedEditorServerDlg::OnBnClickedSave) открывается диалог выбора файла для сохранения текста, практически аналогичный изображенному на рисунке 3.

Рисунок 3. Диалог загрузки документа

1.2.4 Проектирование клиентской части

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

поле редактирования текста документа;

строку ввода IP-адреса сервера;

кнопку подключения/отключения.

Окно диалога клиентской части изображено на рисунке 4.

Рисунок 4. Окно клиента

Главной функцией клиентской части системы является обработчик события изменения текста в поле редактирования документа. Он реализуется функцией CDistributedEditorClientDlg::OnEnChangeText, формирующей транзакции и отправляющей их на сервер.

Приём ответа сервера на транзакции, а также приём от сервера данных о принятых транзакциях других клиентов системы, осуществляется потоковой функцией ReceiveThread. Эта функция в цикле принимает и обрабатывает пакеты с командами от сервера.

При нажатии на кнопку «Подключиться» производится попытка подключения к серверному сокету по адресу, заданному в поле «IP-адрес сервера». Если это удалось, название кнопки изменяется на «Отключиться».

Отключение от сервера происходит при нажатии пользователем кнопки «Отключиться». При этом клиент разрывает соединение с серверным сокетом. После этого интерфейс клиента возвращается к начальному состоянию и можно выполнить повторное подключение к серверу.

Процесс подключения и отключения реализуется функцией CDistributedEditorClientDlg::OnBnClickedConnect.

1.3 Кодирование

Программа была закодирована на языке программирования С++ в среде разработке MS Visual Studio 2005.

Исходный текст программы приведён в приложении А.

2. Тестирование

Правильность работы приложений проверена методом функционального тестирования.

Тестирование проводилось на компьютерах типа IBM PC в операционной системе Windows XP.

Список проводимых тестов:

запуск сервера (см. рис. Б1);

запуск клиента (см. рис. Б2);

загрузка документа (см. рис. Б3);

подключение к серверу (см. рис. Б4);

редактирование текста (см. рис. Б5 и Б6);

сохранение документа (см. рис. Б7).

Результаты тестов приведены в приложении Б.

Заключение

В результате выполнения задания курсового проекта была разработана система распределённого доступа к текстовым документам. Разработка производилась на языке программирования С++ в среде разработки MS Vusial Studio 2005 в операционной системе Windows XP на персональном компьютере IBM PC.

Осуществлено функциональное тестирование разработанного приложения, которое показало корректность его работы.

Приложение А

распределенный доступ текстовый документ

Листинг исходного текста

Сервер

// DistributedEditorServer.h: main header file for the PROJECT_NAME application

//

#pragma once

#ifndef __AFXWIN_H__

#error "include 'stdafx.h' before including this file for PCH"

#endif

#include "resource.h" // main symbols

// CDistributedEditorServerApp:

// See DistributedEditorServer.cpp for the implementation of this class

//

class CDistributedEditorServerApp: public CWinApp

{

public:

CDistributedEditorServerApp();

// Overrides

public:

virtual BOOL InitInstance();

// Implementation

DECLARE_MESSAGE_MAP()

};

extern CDistributedEditorServerApp theApp;

// DistributedEditorServerDlg.h: header file

//

#pragma once

// CDistributedEditorServerDlg dialog

class CDistributedEditorServerDlg: public CDialog

{

// Construction

public:

CDistributedEditorServerDlg(CWnd* pParent = NULL); // standard constructor

// Dialog Data

enum { IDD = IDD_DISTRIBUTEDEDITORSERVER_DIALOG };

protected:

virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV support

// Implementation

protected:

HICON m_hIcon;

// Generated message map functions

virtual BOOL OnInitDialog();

afx_msg void OnPaint();

afx_msg void OnClose();

afx_msg HCURSOR OnQueryDragIcon();

DECLARE_MESSAGE_MAP()

public:

afx_msg void OnBnClickedLoad();

afx_msg void OnBnClickedSave();

void UpdateInformation();

CString m_Text;

};

// DistributedEditorServer.cpp: Defines the class behaviors for the application.

//

#include "stdafx.h"

#include "DistributedEditorServer.h"

#include "DistributedEditorServerDlg.h"

#ifdef _DEBUG

#define new DEBUG_NEW

#endif

// CDistributedEditorServerApp

BEGIN_MESSAGE_MAP(CDistributedEditorServerApp, CWinApp)

ON_COMMAND(ID_HELP, &CWinApp::OnHelp)

END_MESSAGE_MAP()

// CDistributedEditorServerApp construction

CDistributedEditorServerApp::CDistributedEditorServerApp()

{

// TODO: add construction code here,

// Place all significant initialization in InitInstance

}

// The one and only CDistributedEditorServerApp object

CDistributedEditorServerApp theApp;

// CDistributedEditorServerApp initialization

BOOL CDistributedEditorServerApp::InitInstance()

{

// InitCommonControlsEx() is required on Windows XP if an application

// manifest specifies use of ComCtl32.dll version 6 or later to enable

// visual styles. Otherwise, any window creation will fail.

INITCOMMONCONTROLSEX InitCtrls;

InitCtrls.dwSize = sizeof(InitCtrls);

// Set this to include all the common control classes you want to use

// in your application.

InitCtrls.dwICC = ICC_WIN95_CLASSES;

InitCommonControlsEx(&InitCtrls);

CWinApp::InitInstance();

if (!AfxSocketInit())

{

AfxMessageBox(L"Ошибка инициализации библиотеки сокетов");

return FALSE;

}

AfxEnableControlContainer();

// Standard initialization

// If you are not using these features and wish to reduce the size

// of your final executable, you should remove from the following

// the specific initialization routines you do not need

// Change the registry key under which our settings are stored

// TODO: You should modify this string to be something appropriate

// such as the name of your company or organization

SetRegistryKey(_T("Local AppWizard-Generated Applications"));

CDistributedEditorServerDlg dlg;

m_pMainWnd = &dlg;

INT_PTR nResponse = dlg.DoModal();

if (nResponse == IDOK)

{

// TODO: Place code here to handle when the dialog is

// dismissed with OK

}

else if (nResponse == IDCANCEL)

{

// TODO: Place code here to handle when the dialog is

// dismissed with Cancel

}

// Since the dialog has been closed, return FALSE so that we exit the

// application, rather than start the application's message pump.

return FALSE;

}

// DistributedEditorServerDlg.cpp: implementation file

#include "stdafx.h"

#include "DistributedEditorServer.h"

#include "DistributedEditorServerDlg.h"

#ifdef _DEBUG

#define new DEBUG_NEW

#endif

#define BUFFER_SIZE 32000

CDistributedEditorServerDlg *MainDlg; //Указатель на экземпляр класса главного окна

HANDLE MainThread; //Главный поток сервера (подключающий клиентов)

SOCKET MainSocket; //Сокет сервера

int numsoc = 0; //Индекс последнего подключенного клиента

std::map<int,HANDLE> mapThreads; //Карта клиентских потоков

std::map<int,SOCKET> mapSocs; //Карта клиентских сокетов

CString csCurrentText = L""; //Текущая версия документа (текст)

int iVersion = 0; //Текущая версия документа (номер)

int iClients = 0; //Кол-во подключенных клиентов

//Функция клиентского потока

DWORD WINAPI ThreadProc(LPVOID lpParam)

{

int num = (int) lpParam; //Индекс клиента

SOCKET ClientSocket = mapSocs.find(num)->second; //Клиентский сокет

char buff[BUFFER_SIZE]; //Буфер для приёма и отправки данных

int iLen = 0; //Длина принимаемого фрагмента данных

int iClientVers = 0; //Номер клиентской версии документа

int Xb = 0; //Кол-во символов от начала текста, не изменённых транзакцией

int delS = 0; //Кол-во удалённых символов

int insS = 0; //Кол-во добавленных символов

WCHAR *wText = NULL; //Указатель на буфер приёма юникодовского текста

CString csInsertedBlock; //Добавленный блок текста

CString csNewText; //Результат транзакции

iClients++;

MainDlg->UpdateInformation();

while (1) //Цикл приёма команд от клиента

{

ZeroMemory(buff, BUFFER_SIZE);

if (recv(ClientSocket, buff, 3, 0)<3)

break;

if (!strcmp(buff, "CON")) //Подключение клиента

{

//Передача клиенту текущей версии документа (длины текста, версии и самого текста)

iLen = csCurrentText.GetLength();

send(ClientSocket, (const char *) &iLen, 4, 0);

send(ClientSocket, (const char *) &iVersion, 4, 0);

if (iLen)

send(ClientSocket, (const char *) csCurrentText.GetBuffer(), iLen*sizeof(WCHAR), 0);

}

else if (!strcmp(buff, "TRS")) //Обработка транзакции сервером

{

//Приём данных транзакции от клиента

recv(ClientSocket, (char *) &iClientVers, 4, 0);

recv(ClientSocket, (char *) &Xb, 4, 0);

recv(ClientSocket, (char *) &delS, 4, 0);

recv(ClientSocket, (char *) &insS, 4, 0);

wText = (WCHAR*) malloc((insS+1)*sizeof(WCHAR));

ZeroMemory(wText, (insS+1)*sizeof(WCHAR));

if (insS)

recv(ClientSocket, (char*) wText, insS*sizeof(WCHAR), 0);

csInsertedBlock = wText;

if (iClientVers == iVersion) //Сравнение версий (если клиентская версия устарела, транзакция не может пройти)

{

//Транзакция успешна

iVersion++;

send(ClientSocket, "YES", 3, 0); //Уведомление клиента

//Обновление документа

csNewText = csCurrentText.Left(Xb);

csNewText += csInsertedBlock;

csNewText += csCurrentText.Right(csCurrentText.GetLength()-Xb-delS);

csCurrentText = csNewText;

MainDlg->UpdateInformation();

//Рассылка транзакции остальным клиентам

for (std::map<int,SOCKET>::iterator it = mapSocs.begin(); it!= mapSocs.end(); it++)

if (it->second!= ClientSocket)

{

CopyMemory(buff, "TRC", 3);

CopyMemory(&buff[3], &iVersion, 4);

CopyMemory(&buff[7], &Xb, 4);

CopyMemory(&buff[11], &delS, 4);

CopyMemory(&buff[15], &insS, 4);

if (insS)

CopyMemory(&buff[19], csInsertedBlock.GetBuffer(), insS*sizeof(WCHAR));

send(it->second, buff, insS*sizeof(WCHAR) + 19, 0);

}

}

else

{

//Откат транзакции

send(ClientSocket, "NOT", 3, 0); //Уведомление клиента

}

}

else

{

MainDlg->MessageBox(L"Нераспознанная команда", NULL, MB_ICONERROR);

break;

}

}

//Закрытие сокета и отключение от сервера

closesocket(ClientSocket);

mapSocs.erase(num);

mapThreads.erase(num);

iClients--;

MainDlg->UpdateInformation();

return 0;

}

//Функция потока, подключающего клиентов

DWORD WINAPI WorkThread(LPVOID lpParam)

{

SOCKET AcceptSocket;

while (1) //Бесконечный цикл, подключающий новых клиентов

{

AcceptSocket = SOCKET_ERROR;

while (AcceptSocket==SOCKET_ERROR)

AcceptSocket = accept(MainSocket, NULL, NULL);

mapSocs.insert(std::make_pair(numsoc,AcceptSocket));

mapThreads.insert(std::make_pair(numsoc,CreateThread(NULL,0,&ThreadProc,(void*)numsoc,0,NULL)));

numsoc++;

}

return 0;

}

// CDistributedEditorServerDlg dialog

CDistributedEditorServerDlg::CDistributedEditorServerDlg(CWnd* pParent /*=NULL*/)

: CDialog(CDistributedEditorServerDlg::IDD, pParent)

, m_Text(_T(""))

{

m_hIcon = AfxGetApp()->LoadIcon(IDR_MAINFRAME);

}

void CDistributedEditorServerDlg::DoDataExchange(CDataExchange* pDX)

{

CDialog::DoDataExchange(pDX);

DDX_Text(pDX, IDC_TEXT, m_Text);

}

BEGIN_MESSAGE_MAP(CDistributedEditorServerDlg, CDialog)

ON_WM_PAINT()

ON_WM_CLOSE()

ON_WM_QUERYDRAGICON()

//}}AFX_MSG_MAP

ON_BN_CLICKED(IDC_LOAD, &CDistributedEditorServerDlg::OnBnClickedLoad)

ON_BN_CLICKED(IDC_SAVE, &CDistributedEditorServerDlg::OnBnClickedSave)

END_MESSAGE_MAP()

//Функция инициализации диалога (главного окна)

BOOL CDistributedEditorServerDlg::OnInitDialog()

{

sockaddr_in addr;

CDialog::OnInitDialog();

// Set the icon for this dialog. The framework does this automatically

// when the application's main window is not a dialog

SetIcon(m_hIcon, TRUE); // Set big icon

SetIcon(m_hIcon, FALSE); // Set small icon

MainDlg = (CDistributedEditorServerDlg*) AfxGetApp()->m_pMainWnd;

//Создание сокета

MainSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);

if (MainSocket==INVALID_SOCKET)

{

MessageBox(L"Не удалось создать сокет", NULL, MB_ICONERROR);

return FALSE;

}

addr.sin_family = AF_INET;

addr.sin_addr.s_addr = INADDR_ANY;

addr.sin_port = htons(7780);

//Привязка сокета

if (bind(MainSocket,(SOCKADDR*) &addr,sizeof(addr))==SOCKET_ERROR)

{

MessageBox(L"Ошибка привязки сокета", NULL, MB_ICONERROR);

return FALSE;

}

//Прослушивание сокета

if (listen(MainSocket,1)==SOCKET_ERROR)

{

MessageBox(L"Ошибка прослушивания сокета", NULL, MB_ICONERROR);

return FALSE;

}

//Создание главного потока (подключающего клиентов)

MainThread = CreateThread(NULL, 0, &WorkThread, NULL, 0, NULL);

UpdateInformation();

return TRUE;

}

// If you add a minimize button to your dialog, you will need the code below

// to draw the icon. For MFC applications using the document/view model,

// this is automatically done for you by the framework.

void CDistributedEditorServerDlg::OnPaint()

{

CDialog::OnPaint();

}

//Функция закрытия главного окна

void CDistributedEditorServerDlg::OnClose()

{

TerminateThread(MainThread, 0);

closesocket(MainSocket);

CDialog::OnClose();

}

// The system calls this function to obtain the cursor to display while the user drags

// the minimized window.

HCURSOR CDistributedEditorServerDlg::OnQueryDragIcon()

{

return static_cast<HCURSOR>(m_hIcon);

}

//Реакция на нажатие кнопки "Загрузить"

void CDistributedEditorServerDlg::OnBnClickedLoad()

{

CFile file;

CString csFileName;

char *buffer = NULL;

wchar_t *wbuffer = NULL;

int iLen = 0;

if (iClients)

{

MessageBox(L"Текст в стадии редактирования. Загрузка невозможна.", NULL, MB_ICONWARNING);

return;

}

//Диалог выбора файла

CFileDialog filedlg(TRUE, L"txt", NULL, OFN_OVERWRITEPROMPT, L"Текстовый файл|*.txt||", NULL, 0);

if (filedlg.DoModal() == IDOK)

{

//Чтение загружаемого файла

csFileName = filedlg.GetPathName();

file.Open(csFileName, CFile::modeRead, NULL);

iLen = file.GetLength();

buffer = (char*) malloc(iLen+sizeof(WCHAR));

ZeroMemory(buffer, iLen+sizeof(WCHAR));

file.Read(buffer, iLen);

//Распознавание типа текста

if (strlen(buffer) == iLen) //Обычный (Multi-Byte) файл

csCurrentText = buffer;

else //Юникодовский (возможно) файл

{

wbuffer = (wchar_t*) buffer;

csCurrentText = wbuffer;

}

iVersion = 0;

UpdateInformation();

}

}

//Реакция на нажатие кнопки "Сохранить"

void CDistributedEditorServerDlg::OnBnClickedSave()

{

CFile file;

CString csFileName;

//Диалог выбора файла

CFileDialog filedlg(FALSE, L"txt", NULL, OFN_OVERWRITEPROMPT, L"Текстовый файл|*.txt||", NULL, 0);

if (filedlg.DoModal() == IDOK)

{

//Открытие файла и сохранение в него тукущей версии текста

csFileName = filedlg.GetPathName();

file.Open(csFileName, CFile::modeCreate | CFile::modeWrite, NULL);

file.Write(csCurrentText, csCurrentText.GetLength()*sizeof(WCHAR));

}

}

//Обновление элементов диалога (текст и кол-во клиентов)

void CDistributedEditorServerDlg::UpdateInformation()

{

wchar_t NumBuff[12];

SetDlgItemText(IDC_TEXT, csCurrentText);

SetDlgItemText(IDC_CLIENT_NUM, _itow(iClients, NumBuff, 10));

}

Клиент

// DistributedEditorClient.h: main header file for the PROJECT_NAME application

//

#pragma once

#ifndef __AFXWIN_H__

#error "include 'stdafx.h' before including this file for PCH"

#endif

#include "resource.h" // main symbols

// CDistributedEditorClientApp:

// See DistributedEditorClient.cpp for the implementation of this class

//

class CDistributedEditorClientApp: public CWinApp

{

public:

CDistributedEditorClientApp();

// Overrides

public:

virtual BOOL InitInstance();

// Implementation

DECLARE_MESSAGE_MAP()

};

extern CDistributedEditorClientApp theApp;

// DistributedEditorClientDlg.h: header file

//

#pragma once

#include "afxcmn.h"

// CDistributedEditorClientDlg dialog

class CDistributedEditorClientDlg: public CDialog

{

// Construction

public:

CDistributedEditorClientDlg(CWnd* pParent = NULL); // standard constructor

// Dialog Data

enum { IDD = IDD_DISTRIBUTEDEDITORCLIENT_DIALOG };

protected:

virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV support

// Implementation

protected:

HICON m_hIcon;

// Generated message map functions

virtual BOOL OnInitDialog();

afx_msg void OnPaint();

afx_msg HCURSOR OnQueryDragIcon();

DECLARE_MESSAGE_MAP()

public:

afx_msg void OnBnClickedConnect();

public:

CString m_Text;

public:

CIPAddressCtrl m_IP;

public:

afx_msg void OnEnChangeText();

};

// DistributedEditorClient.cpp: Defines the class behaviors for the application.

//

#include "stdafx.h"

#include "DistributedEditorClient.h"

#include "DistributedEditorClientDlg.h"

#ifdef _DEBUG

#define new DEBUG_NEW

#endif

// CDistributedEditorClientApp

BEGIN_MESSAGE_MAP(CDistributedEditorClientApp, CWinApp)

ON_COMMAND(ID_HELP, &CWinApp::OnHelp)

END_MESSAGE_MAP()

// CDistributedEditorClientApp construction

CDistributedEditorClientApp::CDistributedEditorClientApp()

{

// TODO: add construction code here,

// Place all significant initialization in InitInstance

}

// The one and only CDistributedEditorClientApp object

CDistributedEditorClientApp theApp;

// CDistributedEditorClientApp initialization

BOOL CDistributedEditorClientApp::InitInstance()

{

// InitCommonControlsEx() is required on Windows XP if an application

// manifest specifies use of ComCtl32.dll version 6 or later to enable

// visual styles. Otherwise, any window creation will fail.

INITCOMMONCONTROLSEX InitCtrls;

InitCtrls.dwSize = sizeof(InitCtrls);

// Set this to include all the common control classes you want to use

// in your application.

InitCtrls.dwICC = ICC_WIN95_CLASSES;

InitCommonControlsEx(&InitCtrls);

CWinApp::InitInstance();

if (!AfxSocketInit())

{

AfxMessageBox(L"Ошибка инициализации библиотеки сокетов");

return FALSE;

}

AfxEnableControlContainer();

// Standard initialization

// If you are not using these features and wish to reduce the size

// of your final executable, you should remove from the following

// the specific initialization routines you do not need

// Change the registry key under which our settings are stored

// TODO: You should modify this string to be something appropriate

// such as the name of your company or organization

SetRegistryKey(_T("Local AppWizard-Generated Applications"));

CDistributedEditorClientDlg dlg;

m_pMainWnd = &dlg;

INT_PTR nResponse = dlg.DoModal();

if (nResponse == IDOK)

{

// TODO: Place code here to handle when the dialog is

// dismissed with OK

}

else if (nResponse == IDCANCEL)

{

// TODO: Place code here to handle when the dialog is

// dismissed with Cancel

}

// Since the dialog has been closed, return FALSE so that we exit the

// application, rather than start the application's message pump.

return FALSE;

}

// DistributedEditorClientDlg.cpp: implementation file

#include "stdafx.h"

#include "DistributedEditorClient.h"

#include "DistributedEditorClientDlg.h"

#ifdef _DEBUG

#define new DEBUG_NEW

#endif

#define BUFFER_SIZE 32000

CDistributedEditorClientDlg* MainDlg; //Указатель на экземпляр класса главного окна

HANDLE ClientThread; //Клиентский поток

SOCKET ClientSocket; //Клиентский сокет

bool bConnected; //Флаг подключения к серверу

HANDLE hWaitAnswerEvent; //Событие ожидания ответа сервера

CString csCurrentText = L""; //Текущая версия документа (текст)

int iVersion = 0; //Текущая версия документа (номер)

//Функция клиентского потока (для приёма команд сервера)

DWORD WINAPI ReceiveThread(LPVOID lpParam)

{

char buff[4]; //Буфер для приёма команды

int iNewVers = 0; //Номер новой версии документа

int Xb = 0; //Кол-во символов от начала текста, не изменённых транзакцией

int delS = 0; //Кол-во удалённых символов

int insS = 0; //Кол-во добавленных символов

WCHAR *wText = NULL; //Указатель на буфер приёма юникодовского текста

CString csInsertedBlock; //Добавленный блок текста

CString csNewText; //Результат транзакции

while (1) //Цикл приёма команд от сервера

{

ZeroMemory(buff, 4);

if (recv(ClientSocket, buff, 3, 0)<3)

break;

if (!strcmp(buff, "YES")) //Уведомление об успешной транзакции

{

iVersion++;

csCurrentText = MainDlg->m_Text;

SetEvent(hWaitAnswerEvent);

}

else if (!strcmp(buff, "NOT")) //Уведомление об откате транзакции

{

MainDlg->SetDlgItemText(IDC_TEXT, csCurrentText);

SetEvent(hWaitAnswerEvent);

}

else if (!strcmp(buff, "TRC")) //Рассылка транзакции, принятой сервером

{

//Приём данных транзакции

recv(ClientSocket, (char *) &iNewVers, 4, 0);

recv(ClientSocket, (char *) &Xb, 4, 0);

recv(ClientSocket, (char *) &delS, 4, 0);

recv(ClientSocket, (char *) &insS, 4, 0);

wText = (WCHAR*) malloc((insS+1)*sizeof(WCHAR));

ZeroMemory(wText, (insS+1)*sizeof(WCHAR));

if (insS)

recv(ClientSocket, (char*) wText, insS*sizeof(WCHAR), 0);

csInsertedBlock = wText;

if (iNewVers == iVersion + 1) //Клиентская версия предшествует версии сервера

{

iVersion++; //Обновление номера версии

//Обновление текста документа

csNewText = csCurrentText.Left(Xb);

csNewText += csInsertedBlock;

csNewText += csCurrentText.Right(csCurrentText.GetLength()-Xb-delS);

csCurrentText = csNewText;

MainDlg->SetDlgItemText(IDC_TEXT, csCurrentText);

}

else //Клиентская версия либо устарела, либо её значение ошибочно

{

MainDlg->MessageBox(L"Ошибка синхронизации версий", NULL, MB_ICONERROR);

break;

}

}

else

{

MainDlg->MessageBox(L"Нераспознанная команда", NULL, MB_ICONERROR);

break;

}

}

//Закрытие сокета и отключение клиента

closesocket(ClientSocket);

bConnected = false;

MainDlg->m_IP.EnableWindow(true);

MainDlg->SetDlgItemText(IDC_CONNECT, L"Подключиться");

return 0;

}

// CDistributedEditorClientDlg dialog

CDistributedEditorClientDlg::CDistributedEditorClientDlg(CWnd* pParent /*=NULL*/)

: CDialog(CDistributedEditorClientDlg::IDD, pParent)

, m_Text(_T(""))

{

m_hIcon = AfxGetApp()->LoadIcon(IDR_MAINFRAME);

}

void CDistributedEditorClientDlg::DoDataExchange(CDataExchange* pDX)

{

CDialog::DoDataExchange(pDX);

DDX_Text(pDX, IDC_TEXT, m_Text);

DDX_Control(pDX, IDC_IPADDRESS, m_IP);

}

BEGIN_MESSAGE_MAP(CDistributedEditorClientDlg, CDialog)

ON_WM_PAINT()

ON_WM_QUERYDRAGICON()

//}}AFX_MSG_MAP

ON_BN_CLICKED(IDC_CONNECT, &CDistributedEditorClientDlg::OnBnClickedConnect)

ON_EN_CHANGE(IDC_TEXT, &CDistributedEditorClientDlg::OnEnChangeText)

END_MESSAGE_MAP()

//Функция инициализации диалога (главного окна)

BOOL CDistributedEditorClientDlg::OnInitDialog()

{

CDialog::OnInitDialog();

// Set the icon for this dialog. The framework does this automatically

// when the application's main window is not a dialog

SetIcon(m_hIcon, TRUE); // Set big icon

SetIcon(m_hIcon, FALSE); // Set small icon

MainDlg = (CDistributedEditorClientDlg*) AfxGetApp()->m_pMainWnd;

m_IP.SetAddress(127,0,0,1);

bConnected = false;

return TRUE;

}

// If you add a minimize button to your dialog, you will need the code below

// to draw the icon. For MFC applications using the document/view model,

// this is automatically done for you by the framework.

void CDistributedEditorClientDlg::OnPaint()

{

CDialog::OnPaint();

}

// The system calls this function to obtain the cursor to display while the user drags

// the minimized window.

HCURSOR CDistributedEditorClientDlg::OnQueryDragIcon()

{

return static_cast<HCURSOR>(m_hIcon);

}

//Функция реакции на нажатие кнопки Подключиться/Отключиться

void CDistributedEditorClientDlg::OnBnClickedConnect()

{

DWORD ip;

int iLen = 0;

WCHAR *wText = NULL;

if (!bConnected) //Клиент не подключен

{

UpdateData(true);

//Создание сокета

ClientSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);

if (ClientSocket==INVALID_SOCKET)

{

MessageBox(L"Не удалось создать сокет", NULL, MB_ICONERROR);

return;

}

sockaddr_in addr;

addr.sin_family = AF_INET;

m_IP.GetAddress(ip);

addr.sin_addr.s_addr = htonl(ip);

addr.sin_port = htons(7780);

//Подключение к серверу

if (connect(ClientSocket, (SOCKADDR*) &addr, sizeof(addr))==SOCKET_ERROR)

{

MessageBox(L"Ошибка подключения к серверу", NULL, MB_ICONERROR);

return;

}

//Отправка команды подключения и приём текущей версии документа

send(ClientSocket, "CON", 3, 0);

recv(ClientSocket, (char*) &iLen, 4, 0);

recv(ClientSocket, (char*) &iVersion, 4, 0);

wText = (WCHAR*) malloc((iLen+1)*sizeof(WCHAR));

ZeroMemory(wText, (iLen+1)*sizeof(WCHAR));

if (iLen)

{

int iRecvBytes = 0;

do

{

iRecvBytes += recv(ClientSocket, (char*) (((DWORD)wText) + iRecvBytes), iLen*sizeof(WCHAR) - iRecvBytes, 0);

}

while (iRecvBytes < iLen*sizeof(WCHAR));

}

csCurrentText = wText;

SetDlgItemText(IDC_TEXT, wText); //Вывод текущей версии в окне

bConnected = true;

hWaitAnswerEvent = CreateEvent(NULL, TRUE, TRUE, L"WaitAnswerEvent");

//Создание клиентского потока (для приёма команд сервера)

ClientThread = CreateThread(NULL, 0, &ReceiveThread, NULL, 0, NULL);

m_IP.EnableWindow(false);

SetDlgItemText(IDC_CONNECT, L"Отключиться");

}

else //Клиент подключен

{

//Отключение от сервера

TerminateThread(ClientThread, 0);

closesocket(ClientSocket);

bConnected = false;

m_IP.EnableWindow(true);

SetDlgItemText(IDC_CONNECT, L"Подключиться");

}

}

//Функция реакции на событие изменения текста документа

void CDistributedEditorClientDlg::OnEnChangeText()

{

if (bConnected)

{

ResetEvent(hWaitAnswerEvent);

UpdateData(true);

int Xb = 0; //Кол-во символов от начала текста, не изменённых транзакцией

int Xe = 0; //Кол-во символов от конца текста, не изменённых транзакцией

int Lnew = m_Text.GetLength(); //Длина изменённого текста

int Lold = csCurrentText.GetLength(); //Длина первоначального текста

int iTemp = 0; //Длина промежуточной строки

CString csTemp; //Промежуточная строка

char buffer[BUFFER_SIZE]; //Аккумулирующий буфер (для передачи всей транзакции одним блоком)

//Вычисление кол-ва символов от начала текста, не изменённых транзакцией

while (m_Text.Mid(Xb, 1) == csCurrentText.Mid(Xb, 1))

Xb++;

//Определение типа транзакции

if ((Xb<Lnew) && (Xb<Lold)) //Транзакция, заменяющая фрагмент текста (в любом месте текста)

{

//Вычисление кол-ва символов от конца текста, не изменённых транзакцией

while ((m_Text.Mid(Lnew-Xe-1, 1) == csCurrentText.Mid(Lold-Xe-1, 1)) && (Xe < Lold - Xb) && (Xe < Lnew - Xb))

Xe++;

CopyMemory(buffer, "TRS", 3);

CopyMemory(&buffer[3], &iVersion, 4);

CopyMemory(&buffer[7], &Xb, 4);

iTemp = Lold - Xb - Xe; //Кол-во удалённых символов

CopyMemory(&buffer[11], &iTemp, 4);

iTemp = Lnew - Xb - Xe; //Кол-во добавленных символов

CopyMemory(&buffer[15], &iTemp, 4);

csTemp = m_Text.Mid(Xb, iTemp);

CopyMemory(&buffer[19], csTemp.GetBuffer(), iTemp*sizeof(WCHAR));

//Отправка данных транзакции

send(ClientSocket, buffer, iTemp*sizeof(WCHAR) + 19, 0);

}

else if ((Xb<Lnew) && (Xb==Lold)) //Транзакция, добавляющая фрагмент в конец текста

{

CopyMemory(buffer, "TRS", 3);

CopyMemory(&buffer[3], &iVersion, 4);

CopyMemory(&buffer[7], &Xb, 4);

iTemp = 0; //Кол-во удалённых символов

CopyMemory(&buffer[11], &iTemp, 4);

iTemp = Lnew - Xb; //Кол-во добавленных символов

CopyMemory(&buffer[15], &iTemp, 4);

csTemp = m_Text.Right(iTemp);

CopyMemory(&buffer[19], csTemp.GetBuffer(), iTemp*sizeof(WCHAR));

//Отправка данных транзакции

send(ClientSocket, buffer, iTemp*sizeof(WCHAR) + 19, 0);

}

else if ((Xb==Lnew) && (Xb<Lold)) //Транзакция, удаляющая фрагмент в конце текста

{

CopyMemory(buffer, "TRS", 3);

CopyMemory(&buffer[3], &iVersion, 4);

CopyMemory(&buffer[7], &Xb, 4);

iTemp = Lold - Xb; //Кол-во удалённых символов

CopyMemory(&buffer[11], &iTemp, 4);

iTemp = 0; //Кол-во добавленных символов

CopyMemory(&buffer[15], &iTemp, 4);

//Отправка данных транзакции

send(ClientSocket, buffer, 19, 0);

}

//Ожидание ответа сервера на транзакцию

if (WaitForSingleObject(hWaitAnswerEvent, 5000) == WAIT_TIMEOUT)

MessageBox(L"Не удалось дождаться ответа сервера на транзакцию.", L"Ошибка", MB_ICONERROR);

}

}

Приложение Б

Тестирование

Рисунок Б1 - Запуск сервера

Рисунок Б2 - Запуск клиента

Рисунок Б3 - Загрузка документа

Рисунок Б4 - Подключение клиента

Рисунок Б5 - Редактирование текста. Клиент

Рисунок Б6 - Редактирование текста. Сервер

Рисунок Б7 - Сохранение документа в файл

Приложение В

UML диаграммы

Размещено на http://www.allbest.ru

Размещено на http://www.allbest.ru

Рисунок В1 - Диаграмма вариантов использования

Размещено на http://www.allbest.ru

Размещено на http://www.allbest.ru

Рисунок В2 - Диаграмма кооперации

Размещено на http://www.allbest.ru

Размещено на http://www.allbest.ru

Рисунок В3 - Диаграмма деятельности. Формирование транзакции клиентом

Размещено на http://www.allbest.ru

Размещено на http://www.allbest.ru

Рисунок В4 - Диаграмма деятельности. Обработка транзакции сервером

Размещено на http://www.allbest.ru

Размещено на http://www.allbest.ru

Рисунок В5 - Диаграмма последовательности

Размещено на Allbest.ru


Подобные документы

Работы в архивах красиво оформлены согласно требованиям ВУЗов и содержат рисунки, диаграммы, формулы и т.д.
PPT, PPTX и PDF-файлы представлены только в архивах.
Рекомендуем скачать работу.