В этом примере мы разработаем простой сервер и простую клиентскую программу, ведущих между собой "неспешный" диалог. Клиента построим по технике Windows Forms , а сервер - Windows Service . Сервер будет иметь набор готовых маркированных ответов, ждать маркированных запросов клиентов и отвечать им соответствующими сообщениями. Это настроит нас на создание еще более сложной системы - просмотру дистанционных рисунков из БД, которой мы займемся позже.

Создание клиента

Начнем с клиентской программы, которая может запускаться во многих экземплярах ("http://msdn.microsoft.com/ru-ru/library/system.net.sockets.tcplistener.accepttcpclient.aspx ")


Таблица 19.7.
Элемент Свойство Значение
Form Text Client
Size 300; 300
ListBox (Name) listBox
Dock Top
Font Arial; 12pt
Items
  1. Привет!
  2. Лелик
  3. Как жизнь
  4. Оттопыремся сегодня?
  5. Ну тогда пока!
SelectionMode One
Size 292; 119
Button (Name) btnSubmit
AutoSize True
Font Arial; 10pt
Location 96; 127
Size 101; 29
Text Отправить
TextBox (Name) textBox
Dock Bottom
Location 0; 162
Multiline True
ScrollBars Vertical
Size 292; 105

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

using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Drawing; using System.Text; using System.Windows.Forms; // Дополнительные пространства имен using System.IO; using System.Net; using System.Net.Sockets; using System.Threading; namespace SimpleClient { public partial class Form1: Form { int port = 12000; String hostName = "127.0.0.1";// local TcpClient client = null;// Ссылка на клиента public Form1() { InitializeComponent(); // Выделить первый элемент списка listBox.SelectedIndex = 0; listBox.Focus(); // Контекстное меню для очистки TextBox ContextMenuStrip contextMenu = new ContextMenuStrip(); textBox.ContextMenuStrip = contextMenu; ToolStripMenuItem item = new ToolStripMenuItem("Очистить"); contextMenu.Items.Add(item); item.MouseDown += new MouseEventHandler(item_MouseDown); } // Отослать запрос и получить ответ private void btnSubmit_Click(object sender, EventArgs e) { if (listBox.SelectedIndices.Count == 0) { MessageBox.Show("Выделите сообщение"); return; } try { // Создаем клиента, соединенного с сервером client = new TcpClient(hostName, port); // Сами задаем размеры буферов обмена (Необязательно!) client.SendBufferSize = client.ReceiveBufferSize = 1024; } catch { MessageBox.Show("Сервер не готов!"); return; } // Записываем запрос в протокол AddString("Клиент: " + listBox.SelectedItem.ToString()); // Создаем потоки NetworkStream, соединенные с сервером NetworkStream streamIn = client.GetStream(); NetworkStream streamOut = client.GetStream(); StreamReader readerStream = new StreamReader(streamIn); StreamWriter writerStream = new StreamWriter(streamOut); // Отсылаем запрос серверу writerStream.WriteLine(listBox.SelectedItem.ToString()); writerStream.Flush(); // Читаем ответ String receiverData = readerStream.ReadLine(); // Записываем ответ в протокол AddString("Сервер: " + receiverData); // Закрываем соединение и потоки, порядок неважен client.Close(); writerStream.Close(); readerStream.Close(); } // Добавление строки, когда TextBox включен в режиме Multiline private void AddString(String line) { StringBuilder sb = new StringBuilder(textBox.Text); StringWriter sw = new StringWriter(sb); sw.WriteLine(line); textBox.Text = sw.ToString(); } // Очистка списка через контекстное меню void item_MouseDown(object sender, MouseEventArgs e) { textBox.Clear(); listBox.Focus(); } } }

Получив соединение с сервером, мы создаем два потока NetworkStream и упаковываем их в оболочки, удобные для управления чтением/записью. Обмен с сервером отображаем в протоколе TextBox . Для очистки протокола динамически создали контекстное меню.

Класс TcpClient , который мы использовали в коде, является высокоуровневой (и упрощенной) оболочкой сокета (класса Socket ). Если потребуется более низкоуровневое управление сокетом (более детальное), то ссылка на него хранится в свойстве TcpClient.Client . Но поскольку это свойство защищенное (protected ), то доступ к нему возможен только из производного от TcpClient класса.

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

Создание сервера

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



using System; using System.ComponentModel; using System.Configuration.Install; using System.ServiceProcess; namespace SimpleServer { // Во время установки сборки следует вызвать установщик public partial class Installer1: Installer { private ServiceInstaller serviceInstaller; private ServiceProcessInstaller serviceProcessInstaller; public Installer1() { // Создаем настройки для Службы serviceInstaller = new ServiceInstaller(); serviceProcessInstaller = new ServiceProcessInstaller(); // Имя Службы для машины и пользователя serviceInstaller.ServiceName = "SimpleServerServiceName"; serviceInstaller.DisplayName = "SimpleServer"; serviceInstaller.StartType = ServiceStartMode.Manual;// Запуск вручную // Как будет запускаться Служба this.serviceProcessInstaller.Account = ServiceAccount.LocalService; this.serviceProcessInstaller.Password = null; this.serviceProcessInstaller.Username = null; // Добавляем настройки в коллекцию текущего объекта this.Installers.AddRange(new Installer { serviceInstaller, serviceProcessInstaller }); } } }

using System; using System.Collections.Generic; using System.Text; // Дополнительные пространства имен using System.IO; using System.Net; using System.Net.Sockets; using System.Threading; using System.ServiceProcess; using System.Collections; namespace SimpleServer { class Service1: ServiceBase { TcpListener server = null;// Ссылка на сервер int port = 12000; String hostName = "127.0.0.1";// local IPAddress localAddr; String answers = { "1. Ты кто?", "2. Привет, Лелик!", "3. Лучше всех!", "4. Конечно, на полную катушку", "5. До вечера!" }; // Конструктор public Service1() { localAddr = IPAddress.Parse(hostName);// Конвертируем в другой формат Thread thread = new Thread(ExecuteLoop); thread.IsBackground = true; thread.Start(); } private void ExecuteLoop() { try { server = new TcpListener(localAddr, port);// Создаем сервер-слушатель server.Start();// Запускаем сервер String data; // Бесконечный цикл прослушивания клиентов while (true) { if (!server.Pending())// Очередь запросов пуста continue; TcpClient client = server.AcceptTcpClient();// Текущий клиент // Сами задаем размеры буферов обмена (Необязательно!) // По умолчанию оба буфера установлены размером по 8192 байта client.SendBufferSize = client.ReceiveBufferSize = 1024; // Подключаем NetworkStream и погружаем для удобства в оболочки NetworkStream streamIn = client.GetStream(); NetworkStream streamOut = client.GetStream(); StreamReader readerStream = new StreamReader(streamIn); StreamWriter writerStream = new StreamWriter(streamOut); // Читаем запрос data = readerStream.ReadLine(); // Отправляем ответ int index; if (int.TryParse(data.Substring(0, data.IndexOf(".")), out index)) data = answers; else data = data.ToUpper(); writerStream.WriteLine(data); writerStream.Flush(); // Закрываем соединение и потоки, порядок неважен client.Close(); readerStream.Close(); writerStream.Close(); } } catch (SocketException) { } finally { // Останавливаем сервер server.Stop(); } } } }

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

На другой стороне соединения сервер TcpListener в бесконечном цикле прослушивает очередь соединений с клиентами. Если какой-то клиент с ним соединился (server.Pending()!=false ), то сервер извлекает этого клиента методом AcceptTcpClient() - создает сокет для приема/передачи с готовым обратным адресом, создает двунаправленный поток (или два однонаправленных), затем читает запрос и передает ответ.



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

Важное замечание

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

Пример 3. Клиент-серверное приложение просмотра рисунков из БД

На предыдущем простом примере мы познакомились (чуть-чуть) с пронципами создания сетевых приложений. А теперь построим более сложный пример, когда клиент запрашивает рисунки, а сервер извлекает их из хранилища и посылает клиенту. В Упражнении 7 нами было разработано три разных хранилища рисунков и три программы просмотра. В данном примере воспользуемся БД Pictures.my2.mdb с готовыми рисунками и на ее основе создадим сетевое приложение (для тех, кто не делал Упражнение 7 , БД прилагается в каталоге Source/Data ).

Построение клиента

Для клиента построим оконное приложение типа WPF с пользовательским интерфейсом, частично заимствованным из Примера 6 Упражнения 7 .


Сервер не готов, ждите! Мы пытаемся связаться, извините за неудобства...

Для вывода заставки с текстом о неготовности сервера мы применили элемент Viewbox , в который поместили еще один элемент Border с текстовым содержимым. Такой "огород" позволит увеличивать заставку пропорционально размеру окна. Однако введение элемента Viewbox начинает заметно притормаживать перерисовку интерфейса при перемещениях окна, потому что он пытается постоянно пересчитывать масштабы своих дочерних элементов. Имена мы присвоили только тем интерфейсным элементам, которыми собираемся управлять в процедурном коде.

using System; using System.Collections.Generic; using System.Text; using System.Windows; using System.Windows.Controls; using System.Windows.Data; using System.Windows.Documents; using System.Windows.Input; using System.Windows.Media; using System.Windows.Media.Animation; using System.Windows.Media.Imaging; using System.Windows.Shapes; // Дополнительные пространства имен для Stream using System.IO; using IO = System.IO; // Псевдоним для адресации Path using System.Windows.Threading; // Для DispatcherTimer // Дополнительные пространства имен для Socket //using System.Net; using System.Net.Sockets; using System.Collections; // List namespace PicturesClientDB { public partial class Window1: Window { int port = 12000; String hostName = "127.0.0.1"; // local TcpClient client = null; // Ссылка на клиента String sendMessage = "!!!GetNames!!!"; // Запрос на список (позаковыристей) Char separator = { "#" }; // Для преобразования ответа в массив имен DispatcherTimer timer; // Таймер // Конструктор public Window1() { InitializeComponent(); // Создаем и запускаем таймер timer = new DispatcherTimer(); timer.Tick += new EventHandler(timer_Tick); timer.Interval = TimeSpan.FromSeconds(1); timer.Start(); } // Инициирует обращение к серверу void timer_Tick(object sender, EventArgs e) { Execute(listBox); } private void listBox_SelectionChanged(object sender, SelectionChangedEventArgs e) { Execute((ListBox)sender); } void Execute(ListBox lst) { // Заполняем список именами рисунков try { // Если сервер доступен, создаем клиента client = new TcpClient(hostName, port); } catch { // Сервер не готов, запускаем таймер и выходим if (Prompt.Visibility != Visibility.Visible) { Prompt.Visibility = Visibility.Visible; timer.Start(); } return; } switch (sendMessage) { case "!!!GetNames!!!": // Получаем и привязываем имена рисунков к списку lst.ItemsSource = GetNames(); // Выделяем первый элемент списка, чтобы вызвать SelectionChanged lst.SelectedIndex = 0; lst.Focus(); sendMessage = ""; break; default: // Скрываем сообщение и останавливаем таймер if (Prompt.Visibility == Visibility.Visible) { Prompt.Visibility = Visibility.Hidden; timer.Stop(); } // Получаем рисунок и отображаем пользователю кистью String name = lst.SelectedValue.ToString(); BitmapImage bi = new BitmapImage(); bi.BeginInit(); // Получаем от сервера рисунок и обертываем его в поток памяти bi.StreamSource = new MemoryStream(GetPicture(name)); bi.EndInit(); Pictures.picture.ImageSource = bi;// Передаем рисунок фону Border break; } } private String GetNames() { String names; // Создаем потоки сетевых соединений StreamReader readerStream = new StreamReader(client.GetStream()); StreamWriter writerStream = new StreamWriter(client.GetStream()); // Отсылаем запрос серверу writerStream.WriteLine(sendMessage); writerStream.Flush(); // Читаем ответ String receiverData = readerStream.ReadLine(); names = receiverData.Split(separator);// Преобразуем в строковый массив // Закрываем соединение и потоки, порядок неважен client.Close(); writerStream.Close(); readerStream.Close(); return names; } Byte GetPicture(String name) { // Создаем потоки сетевых соединений NetworkStream readerStream = client.GetStream(); StreamWriter writerStream = new StreamWriter(client.GetStream()); // Отсылаем запрос серверу writerStream.WriteLine(name); writerStream.Flush(); // Читаем ответ // ReceiveBufferSize - размер буфера для входящих данных // SendBufferSize - размер буфера для исходящих данных List list = new List(client.ReceiveBufferSize);// С приращением capacity Byte bytes = new Byte; // Размер буфера сокета int count = 0; // Порции входящих данных while ((count = readerStream.Read(bytes, 0, bytes.Length)) != 0) for (int i = 0; i < count; i++) list.Add(bytes[i]); // Преобразуем в массив результата bytes = new Byte; list.CopyTo(bytes); // Закрываем соединение и потоки, порядок неважен client.Close(); writerStream.Close(); readerStream.Close(); return bytes; } } // Для привязки к ресурсу class Pictures { // Поле public static ImageBrush picture = new ImageBrush(); static Pictures() { // Дежурный рисунок заставки picture.ImageSource = new BitmapImage(new Uri(@"flower2.jpg", UriKind.Relative)); picture.Stretch = Stretch.Fill; picture.Opacity = 1.0D; } // Привязываемое в интерфейсе свойство public static ImageBrush Picture { get { return picture; } } } }

Обратите внимание, что при отображении рисунков мы отказались от традиционного элемента Image , как это делали в предыдущем упражнении. А для разнообразия поступили совершенно нетрадиционно (по турецки). Теперь мы рисунки будем отображать кистью ImageBrush в фоне прямоугольника Border через привязанный к нему объект Pictures . Конечно, в жизни так извращаться вряд ли придется, но и такой вариант где-нибудь может пригодиться.


Заставка появится сразу же, как будет обнаружен факт отсутствия или остановки сервера. А после обнаружения сервера заставка исчезнет. Этот механизм немедленно сработает благодаря используемому нами системному таймеру . Однако, сервера пока еще совсем нет и следует его изготовить.

Построение сервера БД как службу
  • Командой File/Add/New Project добавьте к решению NetworkStream новый проект с именем PicturesServerDB типа Windows Service


using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Diagnostics; using System.ServiceProcess; using System.Text; // Дополнительные пространства имен для ADO.NET using System.Data.OleDb; using System.Data.Common; // Дополнительные пространства имен using System.IO; using System.Net; using System.Net.Sockets; using System.Threading; using System.Collections; namespace PicturesServerDB { public partial class Service1: ServiceBase { int port = 12000; String hostName = "127.0.0.1"; // local IPAddress localAddr; TcpListener server = null; // Ссылка на сервер String separator = "#"; // Разделитель имен в строке ответа String connectionString; // Строка соединения с БД public Service1() { // Извлекаем в поле строку соединения с БД из файла App.config connectionString = System.Configuration.ConfigurationManager. ConnectionStrings["PicturesDB"].ConnectionString; // Конвертируем IP в другой формат localAddr = IPAddress.Parse(hostName); // Запускаем в новом потоке (ните) Thread thread = new Thread(ExecuteLoop); thread.IsBackground = true; thread.Start(); } private void ExecuteLoop() { try { server = new TcpListener(localAddr, port);// Создаем сервер-слушатель server.Start();// Запускаем сервер // Бесконечный цикл прослушивания клиентов while (true) { // Проверяем очередь соединений if (!server.Pending())// Очередь запросов пуста continue; TcpClient client = server.AcceptTcpClient();// Текущий клиент // Создаем потоки сетевых соединений StreamReader readerStream = new StreamReader(client.GetStream()); NetworkStream streamOut = client.GetStream(); StreamWriter writerStream = new StreamWriter(streamOut); // Читаем команду клиента String receiverData = readerStream.ReadLine(); // Распознаем и исполняем switch (receiverData) { case "!!!GetNames!!!":// Посылаем имена, разделенные сепаратором String names = GetNames(); writerStream.WriteLine(names); // Используем через оболочку writerStream.Flush(); break; default:// Посылаем рисунок Byte bytes = GetPicture(receiverData); streamOut.Write(bytes, 0, bytes.Length);// Используем напрямую streamOut.Flush(); break; } // Закрываем соединение и потоки, порядок неважен client.Close(); readerStream.Close(); writerStream.Close(); } } finally { // Останавливаем сервер server.Stop(); } } // Извлечение из БД имен рисунков и упаковка их в одну строку для пересылки клиенту string GetNames() { // Создаем и настраиваем инфраструктуру ADO.NET OleDbConnection conn = new OleDbConnection(connectionString); OleDbCommand cmd = new OleDbCommand("SELECT FileName FROM MyTable"); cmd.Connection = conn; conn.Open(); // Извлекаем имена рисунков OleDbDataReader reader = cmd.ExecuteReader(CommandBehavior.CloseConnection); // Формируем строку исходящих данных StringBuilder sb = new StringBuilder(); foreach (DbDataRecord record in reader)// Равносильно чтению reader.Read() sb.Append(((string)record["FileName"]).Trim() + separator); // Соединение здесь закроет сам объект DataReader после прочтения всех данных // в соответствии с соглашением при его создании CommandBehavior.CloseConnection // Удаляем лишний последний символ сепаратора sb.Replace(separator, String.Empty, sb.ToString(). LastIndexOf(separator), separator.Length); return sb.ToString(); } // Извлечение из БД самого рисунка для отправки клиенту byte GetPicture(String name) { // Создаем и настраиваем инфраструктуру ADO.NET OleDbConnection conn = new OleDbConnection(); conn.ConnectionString = connectionString; // Создаем и настраиваем объект команды, параметризованной по имени рисунка OleDbCommand cmd = new OleDbCommand(); cmd.Connection = conn; cmd.CommandType = CommandType.Text; // Необязательно! Установлено по умолчанию cmd.CommandText = "SELECT Picture FROM MyTable WHERE FileName=?"; cmd.Parameters.Add(new OleDbParameter()); cmd.Parameters.Value = name;// Имя рисунка OleDbDataAdapter adapter = new OleDbDataAdapter(cmd); // Извлекаем рисунок из БД DataTable table = new DataTable(); adapter.Fill(table); byte bytes = (byte)table.Rows["Picture"]; // Подключаемся к рисунку return bytes; } } }

Системы "клиент-сервер". Часть 2

Архитектура клиент-сервер: определение, предпосылки для применения, плюсы и минусы

Что такое архитектура клиент-сервер? Варианты построения приложений

Итак, поговорим, наконец, о том,

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

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

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

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

Понятие архитектуры клиент-сервер в системах управления предприятием связано с делением любой прикладной программы на три основных компонента или слоя. Этими тремя компонентами являются

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

    Для локальных приложений, полностью работающих на ПЭВМ (например, Word или Excel), все эти компоненты собраны вместе и не могут быть распределены между различными компьютеры. Такая программа является монолитной и использует для выполнения ресурсы только того компьютера, на котором выполняется.

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

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

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

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

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

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

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

    С точки зрения количества составных частей клиент-серверные системы делятся на двухуровневые и трехуровневые

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

    В третьей части рассмотрен пример трехзвенной структуры Baikonur Server .

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

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

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

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

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

    Предпосылки для появления архитектуры клиент-сервер на предприятии

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

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

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

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

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

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

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

    Архитектура клиент-сервер: Да, но...

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

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

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

    И, наконец, самым большим подводным камнем на пути создания КС системы на предприятии является необходимость менять структуру управления и связанные с этим организационные издержки

    .

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

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

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

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

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

    Сервер может обслуживать нескольких клиентов одновременно. В этом случае говорят о многопользовательском режиме. Только не стоит понимать слово "одновременно" в буквальном смысле. Запросы выполняются сервером последовательно. Если одновременно приходит более одного запроса, то запросы устанавливаются в очередь. В данном случае очередь - это список невыполненных клиентских запросов. Иногда запросы могут иметь приоритеты. Приоритет - это уровень "важности" выполнения запроса. Запросы с более высокими приоритетами должны выполняться раньше.

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

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

    Цикл выполнения запроса состоит из пересылки запроса и ответа между клиентом и сервером и непосредственным выполнением этого запроса на сервере.

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

    Существует концепции построения системы клиент-сервер:

    1) Слабый клиент - мощный сервер - вся обработка информации осуществляется целиком сервером. Сервер посылает готовый результат, не требующий дополнительной обработки. Клиент только ведет диалог с пользователем: составляет запрос, отсылает запрос, принимает запрос и выводит информацию на экран (на принтер, в файл).

    2) Сильный клиент - часть обработки информации перепоручается клиенту.

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

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

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

    При частичной обработке данных на клиенте "время ожидания" меньше. Меньше оно за счет упрощения запроса и времени его выполнения. Отсюда меньше ожидание в очереди для исполнения запроса.

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

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

    P.S. Нередко в ряде организаций в качестве сервера сознательно используют устаревшие компьютеры. На них хорошо размещать файловые хранилища, принт-серверы (к нему подключается офисный принтер), WEB-серверы (Интернет-серверы), небольшие базы данных (серверную часть). Это оправдано с экономической точки зрения.

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

    Сергей СОКОЛОВ (БГУИР)

    БД, работающие по технологии ФАЙЛ-СЕРВЕР;

    БД, работающие по технологии КЛИЕНТ-СЕРВЕР.

    Файл-сервер


    - Обращение к БД (запрос)
    - Перекачка данных с блокировкой доступа других пользователей
    - Обработка данных на компьютере пользователя

    Для наглядности рассмотрим конкретные примеры. Допустим, Вам необходимо просмотреть отправленные платежные поручения за период с 19 по 25 мая на сумму 5000 рублей. Пользователю необходимо будет запустить на своем компьютере клиентское приложение, работающее в БД с платежными поручениями, и ввести нужные критерии отбора. После чего на Ваш компьютер перекачается с сервера базы данных и загрузится в оперативную память файл, содержащий все документы данного вида за весь период на любые суммы. Запущенное на компьютере пользователя клиентское приложение, работающее с БД, само проведет обработку этой информации (отсортирует их), после чего выдаст ответ (на экране появится список платежных поручений, удовлетворяющих Вашим критериям). После этого Вы выберете нужное платежное поручение и попытаетесь отредактировать (изменить) в нем одно поле - например, дату. Во время редактирования происходит блокировка источника данных, то есть всего файла, содержащего этот документ. Это означает, что файл будет либо совсем не доступен остальным пользователям, либо доступен только в режиме просмотра. Причем подобного рода захват происходит даже не на уровне записи, то есть одного документа, а заблокированным является целый файл - то есть вся таблица, содержащая аналогичные документы. Только после полной обработки этого поля и выхода из режима редактирования данный файл платежных поручений будет разблокирован от захвата пользователем. Если же данные хранятся в более объемных объектах, например, в одном файле содержатся платежные поручения и о поступлении средств, и об отправке, то еще большая часть информации будет не доступна. Вы будете работать с одним полем "дата" в одном документе - остальные сотрудники предприятия будут ждать, пока Вы не закончите.

    Недостатки ФАЙЛ-СЕРВЕРНОЙ системы очевидны:

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

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

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

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

      Клиент-сервер

      Обработка запроса одного пользователя:
      - Обращение к БД (SQL-запрос)
      - Передача ответа - результата обработки


      При необходимости произвести обработку информации, хранящейся в БД, запущенное на компьютере пользователя клиентское приложение, работающее с БД, формирует запрос на языке SQL (название от начальных букв - Structured Query Language). Сервер базы данных принимает запрос и обрабатывает его самостоятельно. Никакой массив данных (файл) по сети не передается. После обработки запроса на компьютер пользователя передается только результат - то есть, в предыдущем примере, - список платежных поручений, удовлетворяющих нужным критериям. Сам же файл, в котором хранились данные, послужившие источником для обработки, остается незаблокированным для доступа самого сервера по запросам других пользователей.

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

      Таким образом, все вышеперечисленные недостатки ФАЙЛ-СЕРВЕРНОЙ схемы устраняются в архитектуре КЛИЕНТ-СЕРВЕР:

        Массивы данных не перекачиваются по сети от сервера БД на компьютер пользователя. Требования к пропускной способности сети понижаются. Это делает возможным одновременную работу большого числа пользователей с большими объемами данных.

        Обработка данных осуществляется на сервере БД, а не в компьютере пользователей. Что позволяет использовать более простые, а значит, дешевые компьютеры на клиентских местах.

        Блокировки (захвата) данных одним пользователем не происходит.

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

        Рассмотрев отличие ФАЙЛ-СЕРВЕРА от КЛИЕНТ-СЕРВЕРА, можно завершить рассмотрение понятия "хранилище информации". Важно подчеркнуть, что от вида используемой СУБД во многом зависит работа корпоративной системы. Совершенно очевидно, что для крупных предприятий, с большим количеством пользователей, с огромным числом записей в БД, файл-серверная схема совершенно неприемлема. С другой стороны, отличия в базах данных есть и по другим параметрам и возможностям:

          типам данных, которые могут храниться в БД (числа, даты, текст, рисунки, видео, звук и т.д);

          по организуемым самой БД технологиям доступа к данным в базе и уровню защиты информации от несанкционированного доступа;

          по предоставляемым средствам и методикам разработки, которые могут быть применены для проектирования какой-либо информационной системы на основе данной БД;

          по предоставляемым средствам и методикам анализа информации (данных), которые могут быть применены в информационной системы на основе данной БД;

          по надежности и устойчивости, то есть (грубо) количеству записей (заполненных полей) в БД, при которых обеспечивается надежная и бесперебойная возможность доступа, изменения, анализа информации в БД;

          по быстродействию - времени, затраченному на доступ и обработку информации;

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

          по уровню поддержки (сервиса), предоставляемого разработчиком базы данных или его авторизованным дилером;

          по наличию хороших средств создания приложений, использующих данную базу данных и т.д.

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

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

    Серверами называются процессы, отвечающие за поддержку и файловой системы, а клиентами - процессы, которые посылают запрос и ожидают ответ от сервера.

    Модель клиент-сервер используется при построении системы на основе СУБД, а также почтовые системы. Существует еще так называемая файл-серверная архитектура, которая существенно отличается от клиент-серверной.

    Данные в файл-серверной системе сохраняются на файловом сервере (Novell NetWare или WindowsNT Server), а обрабатываются они на рабочих станциях посредством функционирования "настольных СУБД", таких как Access, Paradox, FoxPro и т.п.

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

    Технология клиент-сервер реализована функционированием двух (как минимум) приложений - клиентов и сервера, которые делят функции между собой. За хранение и непосредственное манипулирование данных отвечает сервер, примером которого может быть SQLServer, Oracle, Sybase и другие.

    Пользовательский интерфейс формирует клиент, в основе построения которого используются специальные инструменты или настольные СУБД. Логическая обработка данных выполняется частично на клиенте, и частично на сервере. Посылка запросов на сервер выполняется клиентом, обычно на языке SQL. Полученные запросы обрабатываются сервером, и клиенту (клиентам) возвращается результат.

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

    Преимущества архитектуры клиент-сервер

    Технология клиент-сервер привносит в информационную систему такие качества:

    • Надежность

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

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

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

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

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

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

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

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