Общие принципы дизайна в объектно

advertisement
Общие принципы дизайна в объектно-ориентированном
проектировании
Задание: Разработка ООП-приложения на платформе .NET. Передача информации в глобальной сети
Реализовать сервис для вычисления значений некоторого выражения и
программу, которая обращается к этому сервису с целью получения результатов вычисления. Формула выбирается из таблицы:
n
r  e
i 0
n
r 
i 0
xi
1  sin  x  i 
2  cos  x  i 
n
r   1  sin  x  i 
i 0
n
r
i 1
1
x i
n
r   sin  x  i 
i 0
n
i
i 0 i  1
r
n
r   ln 1  x  i 
i 0
n
r   tg  x  i 
i 0
Минимальные требования к лабораторной работе
 Серверная часть должна ожидать (правильный термин прослушивать)
соединения клиентов, после установления соединения получить данные для
вычисления от клиентской части, вычислить значение по формуле и передать
его запросившему клиенту.
 Клиентская часть должна быть выполнена в виде приложения
Windows Forms.
 Окно запроса клиентской части должно содержать текстовые поля для
ввода данных, участвующих в вычислении, кнопку расчета и текстовое поле
вывода результата.
 Клиентская часть считывает данные из текстовых полей, отправляет
данные серверной части и выводит полученный результат.
1
Методические указания
Откройте среду Visual Studio. Создайте новый проект, консольное приложение, которое будет серверной частью вычислительного комплекса. Для
этого выберите пункт меню File | New | Project. Появится окно выбора нового
проекта, показанное на рис. 1.1. Среда Visual Studio изначально предназначена для разработки, отладки и тестирования большого числа проектов одновременно. Все проекты, загруженные в Visual Studio, объединяются в Решение, которое хранит общую конфигурацию проектов, последовательность построения и запуска программ при отладке. Поскольку предполагается разрабатывать две программы одновременно, то будет лучше, если Visual Studio
создаст отдельный каталог для решения, внутри которого будут располагаться каталоги создаваемых проектов. Выделите консольное приложение на
языке C# и дайте ему имя, например Server (см. рис. 1). Установите флажок,
предписывающий Visual Studio создать отдельный каталог для решения, и
укажите имя решения, например CalculationService. Если флажок не будет
установлен, файл решения будет сохранен в каталоге создаваемого проекта.
Рис. 1
После нажатия на кнопку Ok будет создан консольный проект с именем
Server внутри решения CalculationService, в чем можно убедиться, открыв
проводник решения (Ctrl + Alt + L) – самый важный инструмент управления
проектами. Теперь добавьте еще один новый проект в решение. Для этого, в
контекстном меню решения выберите пункт Add | New Project. В появившемся окне укажите проект приложения с графическим интерфейсом пользователя – Windows Application, назовите его Client и нажмите Ok (рис. 2).
2
Рис. 2
Обратите внимание, проект, который был создан первым, отображается
в дереве решения жирным шрифтом, это связано с тем, что по умолчанию
Visual Studio назначает этот проект запускаемым. Назначение проектов на
запуск можно легко менять по своему усмотрению.
Рассмотрение взаимодействия двух программ по сети будет рассмотрено
на примере передачи данных файла от клиента к серверу. Такой пример позволит показать ряд полезных классов, методов и свойств платформы .NET
Framework.
Перетащите на форму клиентского приложения компонент диалогового
окна открытия файла OpenFileDialog с панели инструментов Toolbox (Ctrl +
Alt + X). Компонент OpenFileDialog находится на вкладке Dialogs и продублирован на вкладке All Windows Forms (рис. 3).
3
Рис. 3
Перетащите еще три элемента управления: Label, TextBox и Button. Дайте этим элементам осмысленные имена в редакторе свойств (F4), измените
свойство Text каждого элемента, как показано на рис. 4. Сделайте двойной
щелчок на кнопке, для генерации текста обработчика события нажатия на
кнопку. Появится метод, внутри которого будут запрограммированы операции, необходимые для отправки файла, заданного пользователем, на сервер.
Рис. 4
4
Наберите или скопируйте текст, показанный ниже внутрь данного обработчика.
if (openFileDialog.ShowDialog() == DialogResult.OK)
{
try
{
TcpClient tcpClient = new TcpClient(hostName.Text, 10000);
using (FileStream fileStream = new FileStream(openFileDialog.FileName,
FileMode.Open))
{
using (NetworkStream networkStream = tcpClient.GetStream())
{
BinaryWriter writer = new BinaryWriter(networkStream);
writer.Write(1);
//Начало передачи данных.
writer.Write(Path.GetFileName(fileStream.Name)); //Передача имени
файла.
writer.Write(fileStream.Length);
long sendsByte = 0;
long lengthBuffer = 4096;
byte[] buffer = new byte[lengthBuffer];
//Передача длины файла.
//Количество переданных байт.
//Длина буфера данных.
//Буфер для хранения данных.
while (sendsByte < fileStream.Length)
{
int length = (int)Math.Min(lengthBuffer, fileStream.Length sendsByte);
fileStream.Read(buffer, 0, length);
writer.Write(buffer, 0, length);
sendsByte += length;
}
writer.Write(0);
//Чтение данных из файла.
//Передача данных серверу.
//Признак конца связи с
сервером.
}//Здесь происходит закрытие потока networkStream и объекта tcpClient.
}//Здесь происходит закрытие файла fileStream.
MessageBox.Show(string.Format("Файл {0} успешно передан серверу",
openFileDialog.FileName), "Передача
файла");
}
catch (SystemException exception)
{
MessageBox.Show(exception.Message, "Ошибка при передачи файла",
MessageBoxButtons.OK,
MessageBoxIcon.Error);
}
}
В первой строке обработчика нажатия на кнопку происходит вызов диалогового окна открытия файла. Если пользователь после выбора файла нажал
кнопку Ok, то метод ShowDialog() возвращает значение OK перечисления
DialogResult, управление в этом случае передается внутрь оператора if.
В строке TcpClient tcpClient = new TcpClient(hostName.Text, 10000); происходит соединение клиента с сервером. Для этого считывается строка с
именем хоста сервера из текстового поля (из элемента управления TextBox,
которому было присвоено имя hostName). Соединение устанавливается по
заданному порту, в данном случае 10000. Поскольку одновременно может
быть запущено несколько программ, работающих с сетью, то с одним хостом
(компьютером) ассоциируется пространство портов TCP/IP, которое лежит в
диапазоне от 0 до 65535. Кроме того, в операционной системе Windows порты с номерами 0-1023 зарезервированы для стандартных служб и глобальных
5
сервисов. Поэтому для новых программ порты с этими номерами использовать не рекомендуется.
В следующих двух строках открываются потоки ввода/вывода файла и
удаленного сервера. Данные объекты являются критическими ресурсами, поэтому после окончания работы с ними их следует закрыть, вызвав один из
методов Close() или Dispose(). Язык C# имеет специальный оператор using
(не путать с директивой using), позволяющий оформить вызов очистки ресурсов. В остальных строках происходит передача данных файла через локальный буфер. Работа сервера и клиента построена по принципу конечного
автомата, клиент записывает команды и данные в поток, а сервер считывает
команды из потока, и по ним определяет, как интерпретировать данные.
Уместно напомнить, что передача по сети происходит в двух направлениях,
но в данном примере клиент ничего не пытается получить от сервера. Потоки
FileStream и NetworkStream являются двоичными, т. е. они могут записывать
и считывать массивы байтов, что не всегда удобно. Поэтому здесь был применен класс BinaryWriter, который позволяет записывать простые типы данных в поток как двоичные значения. Аналогичный класс для чтения данных
из потока называется BinaryReader. Данные классы хоть и не являются критическими ресурсами, тем не менее они всегда ссылаются на критический
ресурс, получая его в конструкторе. Вызов методов Close() или Dispose() этих
классов ничего не делает с ними, а только лишь вызывает одноименные методы тех потоков ввода/вывода, на которые они ссылаются. Поэтому их инициализация не оформляется внутри оператора using. Полную информацию о
классах TcpClient, FileStream, NetworkStream, BinaryWriter, BinaryReader,
TcpListener и др. можно узнать из справочной системы MSDN. Для этого
установите текстовый курсор на интересуемом классе и нажмите F1. Откроется справочная система на английском или русском языке, в зависимости от
установленной версии.
Перейдите на вкладку Program.cs проекта сервера и внутри функции
main наберите или скопируйте следующий текст:
int port = 10000;
IPAddress localAddress = IPAddress.Parse("127.0.0.1"); //Специальный IP-адрес,
//ссылающийся на локальный
хост.
TcpListener tcpListener = new TcpListener(localAddress, port);
tcpListener.Start();
//Запуск прослушивания.
using (TcpClient tcpServer = tcpListener.AcceptTcpClient())
{
BinaryReader reader = new BinaryReader(tcpServer.GetStream());
while (true)
{
int code = reader.ReadInt32();
switch (code)
{
case 1: //Код передачи данных.
{
string fileName = reader.ReadString();
//Имя передаваемого
файла.
using (FileStream fileStream = new
FileStream(Path.GetFullPath(fileName),
FileMode.Create))
{
6
long lengthFile = reader.ReadInt64();
//Длина получаемого
long receivesByte = 0;
//Количество
long lengthBuffer = 4096;
//Длина буфера
byte[] buffer = new byte[lengthBuffer];
//Буфер для хранения
файла.
полученных байт.
данных.
данных.
while (receivesByte < lengthFile)
{
int length = (int)Math.Min(lengthBuffer, lengthFile –
receivesByte);
reader.Read(buffer, 0, length);
//Получение данных от
киента.
fileStream.Write(buffer, 0, length); //Запись данных в
файл.
receivesByte += length;
}
Console.WriteLine("Файл {0} успешно передан от клиента",
fileName);
}
break;
}
case 0:
{
//Код конца связи.
tcpListener.Stop();
return;
//Останавливает прослушивание.
//Выход из программы.
}
default:
{
Console.WriteLine("Из потока считан неправильный код {0}", code);
tcpListener.Stop();
return;
//Останавливает прослушивание.
//Выход из программы.
}
}
}
}
Сервер прослушивает порт 10000 локального хоста до тех пор, пока клиент не соединиться. После соединения с клиентом метод AcceptTcpClient
возвращает управление, получив объект типа TcpClient. Затем сервер в цикле
считывает код команды: 1 – передача данных, 0 – завершение связи. Такая
организация при необходимости позволит передать несколько файлов за
один сеанс связи. Если сервер принял код команды 1, то он считывает из потока имя файла и создает его в текущем каталоге (функция Path.GetFullPath()
вычисляет полный путь по отношению к заданному имени файла и текущему
пути). Запись файла происходит в таком же порядке, в каком отправил его
клиент, поэтому к моменту окончания записи файла точно известно, что
дальше необходимо опять считывать код команды. Если сервер принял код
команды 0, то прослушивание останавливается и программа завершает работу.
Теперь можно перейти к компиляции двух написанных программ. Программы для платформы .NET Framework всегда компилируются средствами
самой платформы. Это первая платформа, в которой встроены самые мощные
средства компиляции и поддержки разработки. Платформа содержит исполняемые файлы, реализующие обращение к классам компиляции для четырех
языков: C# (csc.exe), Visual Basic (vbc.exe), JScript (jsc.exe), Visual J# (vjc.exe).
7
Кроме того, в состав .NET Framework, начиная с версии 2.0 и выше, включено средство автоматизированного построения сборок MSBuild.exe, которое
существенно упрощает сборку продукта. Среда Visual Studio при построении
решения пользуется средствами MSBuild.
Для компиляции двух написанных программ из Visual Studio необходимо построить решение. Среда Visual Studio поддерживает много проектов,
для которых сборка продукта включает не только компиляцию, но и другие
действия, поэтому применяется термин «Построить решение». Для построения решения необходимо нажать сочетание клавиш Ctrl + Shift + B. В окне
Output (Ctrl + Alt + O) появится отчет о построении и найденных ошибках.
Перейдите в каталог серверного приложения, внутри которого должен
лежать файл Program.cs. Создайте в этом каталоге файл csc.bat с помощью
любого файлового менеджера. Откройте его и наберите следующую строку:
C:\WINDOWS\Microsoft.NET\Framework\v2.0.50727\csc.exe %1 %2 %3
%4 %5
Сохраните файл.
В командной строке наберите: csc.bat /out:server.exe Program.cs и нажмите Enter. В текущем каталоге появится файл server.exe.
Перейдите в каталог клиентской программы и скопируйте в него файл
csc.bat. Для компиляции приложения наберите и выполните следующую
строку:
csc.bat /target:winexe /out:client.exe Program.cs MainForm.cs MainForm.Designer.cs
В текущем каталоге появится файл client.exe.
Дополнительную информацию по параметрам командной строки можно
получить в MSDN на русском или английском языках или набрав в командной строке csc.bat /?.
Ввод данных из текстовых полей Windows Forms
Для ввода данных из текстовых полей лучше воспользоваться статическим методом Parse() классов int, double, float и т. д. Этот метод генерирует
три типа исключения, если не может преобразовать строку к значению заданного типа. Поэтому для простоты предлагается заключить метод Parse() в
блок перехвата исключения try…catch:
try
{
double d = double.Parse(textBox.Text);
}
catch (SystemException exception)
{
MessageBox.Show(exception.Message, "Ошибка");
}
Полезные классы
Random
Представляет генератор псевдослучайных чисел
Math
Предоставляет константы и статические методы для тригонометрических, логарифмических и иных общих математических функций
8
Выполняет операции для экземпляров класса String, содержащих сведения пути к файлу или папке
Environment
Предоставляет сведения о текущей среде и платформе, а
также средства управления ими
SystemInformation Предоставляет сведения относительно текущей операционной системы
Path
9
Download