НОУ ІНТУЇТ | лекція | Робота з потоками даних

  1. Приклад 7. Використання бази даних з малюнками
  2. Завдання до вправи
  3. Вправа 8. Мережеві додатки на базі потоку NetworkStream
  4. Приклад 1. Створення простої тестової Служби Windows

Приклад 7. Використання бази даних з малюнками

Тепер створимо утиліту для перегляду малюнків з отриманої раніше БД.

<Window x: Class = "App7.Window7" xmlns = "http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns: x = "http://schemas.microsoft.com/winfx/2006 / xaml "Title =" Приклад 7. Використання БД з малюнками "Height =" 300 "Width =" 470 "MinHeight =" 300 "MinWidth =" 450 "Name =" _ Window7 "Closing =" Window_Closing "> <Grid> <Grid .ColumnDefinitions> <ColumnDefinition Width = "*" /> <ColumnDefinition Width = "110" /> </Grid.ColumnDefinitions> <Border Background = "Black"> <Image Name = "image" Stretch = "Fill" /> < / Border> <ListBox Name = "listBox" Grid.Column = "1" Padding = "5,0" ScrollViewer.VerticalScrollBarVisibility = "Visible" SelectionChanged = "listBox_SelectionChanged"> </ ListBox> </ Grid> </ Window> 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.Imaging; using System.Windows.Shapes; // Додаткові простору імен using FORMS = System.Windows.Forms; // Ім'я користувача using System.IO; using System.Configuration; // Для SettingsProperty using IO = System.IO; // Ім'я для адресації Path // Додаткові простору імен для ADO.NET using System.Data; using System.Data.OleDb; using System.Data.Common; namespace App7 {public partial class Window7: Window {String nameDB; // Опорна поле для властивості з ім'ям БД int count; // Поле під число малюнків string [] names; // Поле під імена малюнків Properties.Settings settings; // Параметри // Конструктор public Window7 () {InitializeComponent (); // Запускаємо діалог вибору БД nameDB = SelectDB (); // При відмові від вибору нічого не запускаємо, // а в головному вікні Window1 закриваємо це вікно if (nameDB! = String.Empty) ExecuteWindow (); } // Витягуємо малюнки і управляємо показом private void ExecuteWindow () {// Читаємо деякі дані з БД OleDbConnection conn = new OleDbConnection (ConnectionString (nameDB)); OleDbCommand cmd = new OleDbCommand ( "SELECT COUNT (*) FROM MyTable"); cmd.Connection = conn; conn.Open (); // Витягуємо число малюнків count = (int) cmd.ExecuteScalar (); // Міняємо команду і витягаємо імена малюнків cmd.CommandText = "SELECT FileName FROM MyTable"; OleDbDataReader reader = cmd.ExecuteReader (CommandBehavior.CloseConnection); names = new string [count]; int i = 0; while (reader.Read ()) // Рівносильно foreach (DbDataRecord rec in reader) names [i ++] = ((string) reader [ "FileName"]). Trim (); // Відразу прибираємо прогалини // З'єднання тут закриє сам об'єкт DataReader після прочитання всіх даних // відповідно до угоди при його створенні CommandBehavior.CloseConnection // Прив'язуємо масив імен малюнків до списку listBox.ItemsSource = names; // Виділяємо перший елемент списку, щоб викликати SelectionChanged listBox.SelectedIndex = 0; listBox.Focus (); } // Рядок з'єднання з БД private String ConnectionString (String fileName) {// string pathToFile = Application.StartupPath.ToString (); string JetEngineString = "Provider = Microsoft.Jet.OLEDB.4.0;" + "Jet OLEDB: Engine Type = 5; Data Source ="; return JetEngineString + fileName; } // При зміні індексу списку показує обраний малюнок private void listBox_SelectionChanged (object sender, SelectionChangedEventArgs e) {// Створюємо та налаштовуємо інфраструктуру ADO.NET OleDbConnection conn = new OleDbConnection (); conn.ConnectionString = ConnectionString (nameDB); // Створюємо та налаштовуємо об'єкт команди, параметризрвані на ім'я малюнка OleDbCommand cmd = new OleDbCommand (); cmd.Connection = conn; cmd.CommandType = CommandType.Text; // Не обов'язково! Встановлено за замовчуванням cmd.CommandText = "SELECT Picture FROM MyTable WHERE FileName =?"; cmd.CommandTimeout = 1; // Намагатися X секунд, потім згенерувати помилку (= 30 сек) cmd.Parameters.Add (new OleDbParameter ()); cmd.Parameters [0] .Value = ((ListBox) sender) .SelectedValue.ToString (); // Ім'я малюнка OleDbDataAdapter adapter = new OleDbDataAdapter (cmd); // Витягуємо малюнок з БД DataTable table = new DataTable (); adapter.Fill (table); byte [] bytes = (byte []) table.Rows [0] [ "Picture"]; // Підключаємося до малюнка // Відображаємо малюнок користувачеві BitmapImage bi = new BitmapImage (); bi.BeginInit (); bi.StreamSource = new MemoryStream (bytes); // обгортають витягнутий малюнок в потік bi.EndInit (); image.Source = bi; } // Властивість доступу, перевіряється в Window1 public String NameDB {get {return nameDB; }} // Діалог вибору БД. Повертає // порожньо, якщо не вибрано БД private String SelectDB () {settings = new App7.Properties.Settings (); String path = settings.dialogOpen; String fileName = IO.Path.Combine (path, "Pictures.my2.mdb"); // Створюємо та налаштовуємо діалог вибору файлу FORMS.OpenFileDialog openFileDialog = new FORMS.OpenFileDialog (); openFileDialog.InitialDirectory = path; openFileDialog.FileName = ""; openFileDialog.Filter = "DB Files (*. my2.mdb) | * .my2.mdb"; FORMS.DialogResult result = openFileDialog.ShowDialog (); if (result! = FORMS.DialogResult.Cancel) fileName = openFileDialog.FileName; // Повне ім'я else fileName = String.Empty; return fileName; } // Обробник події Closing, спрацьовує перед закриттям програми private void Window_Closing (object sender, System.ComponentModel.CancelEventArgs e) {// Зберігаємо шлях в параметрах if (nameDB! = String.Empty) {settings.dialogOpen = nameDB.Substring ( 0, nameDB.LastIndexOf ( '\\')); settings.Save (); }}}}

Властивість CommandTimeout об'єкта OleDbCommand отримує або задає час очікування (в секундах) перед завершенням спроби виконання команди і створенням помилки. За умовчанням встановлено 30 секунд, що відповідає великій мережі складних запитах. Для нашої задачі і 1 секунди дуже багато, але менше не можна, оскільки це властивість типу int.

void Example7 () {// Запобігання повторного створення дочірнього вікна bool existsWindow = false; foreach (Window window in Application.Current.Windows) // Перебираємо колекцію {if (window.Name == "_Window7") {// Знайшли серед існуючих existsWindow = true; window.Activate (); // Перенести передній план break; // Перервати перебір}} if (! existsWindow) // Якщо не існує, то створити і показати {Window7 wnd = new Window7 (); // Перед показом перевіряємо, обрана чи БД if (wnd.NameDB! = String.Empty) wnd.Show (); // Показуємо вікно Window7 else wnd.Close (); // Закриваємо вікно Window7}}

Все, ілюстрацію потоку MemoryStream ми закінчили і попутно ще де в чому потренувалися. Решта невикористані пункти списку головного вікна в файлі Window1.xaml можна видалити, як і відповідний їм процедурний код у файлі Window1.xaml.cs. Або нехай так і бовтаються.

Завдання до вправи

Наостанок можна зробити ще одне зауваження до наведеного коду Вправи 7. У вікнах перегляду Window3, Window5 і Window7 ми створювали обробник події Closing і старанно зберігали змінну dialogOpen в параметрах програми. Змінну dialogFolder ми теж зберігаємо, але тільки при закритті головного вікна. Звідси дізгармонія (фізгармоніка): поки не закрили головне вікно, при виконанні парних прикладів діалог вибору каталогів завжди стартує з того місця, яке було збережено при закритті головного вікна в останньому запуску.

Зберігати шлях і змінну dialogFolder можна відразу після видачі повідомлення MessageBox.Show () про вдалий створенні файлової бібліотеки або БД.

Вправа 8. Мережеві додатки на базі потоку NetworkStream

Ще одним нащадком абстрактного класу Stream є мережевий потік NetworkStream простору імен System.Net.Sockets бібліотеки System.dll. Можна використовувати клас NetworkStream для виконання як синхронної, так і асинхронної передачі даних. У синхронному режимі перед поверненням управління викликає програмі виклики функцій, які виконують мережеві операції, очікують закінчення виконання цих операції. В асинхронному режимі виклики негайно повертаються ініціював їх коду.

Розглянемо застосування NetworkStream на прикладі спільної роботи декількох запущених копій клієнтської програми і однієї копії серверного. Роботу побудуємо на основі створеної раніше БД Pictures.my2.mdb з малюнками. Сервер буде мати доступ до цієї бази і посилати всі необхідні дані клієнтам за їх запитом. Інтерфейсне вікно клієнта реалізуємо за технологією Windows Forms. А ось сервер створимо як резидентную програму (Службу Windows) і запустимо в фоновому режимі. Протокол обміну для контролю будемо виводити в текстовий файл. Так ми максимально разнообразим демонстрацію застосування потоків.

Кроки виконання вправи оформимо у вигляді прикладів. Спочатку познайомимося з деякими поняттями, взятими з Вікіпедії - вільної енциклопедії.

Сокет (Socket) - програмний інтерфейс (API - набір програм) для забезпечення інформаційного обміну між процесами (загрущеннимі в ОЗУ додатками, які вже виконуються процесором). Потокові сокети працюють з установкою з'єднання. Вони забезпечують надійну ідентифікацію обох сторін, гарантуючи цілісність і успішність доставки даних, і спираються на протокол TCP. Умовно можна уявити собі сокети у вигляді двох роз'ємів, до яких включено канал зв'язку, призначений для передачі даних через мережу (потік даних). При передачі даних сокет можна вважати резервним сховищем - аналогом файлу. Дані між сокетами перекачує потік.

TCP (Transmission Control Protocol - протокол управління передачею) - один з основних мережевих протоколів Інтернету, призначений для управління передачею даних в мережах і підмережах TCP / IP. Це транспортний механізм, що надає потік даних, з попередньою установкою з'єднання, за рахунок цього дає впевненість в достовірності одержуваних даних. TCP здійснює повторний запит даних в разі втрати даних і усуває дублювання при отриманні двох копій одного пакета. TCP гарантує, що додаток отримає дані точно в такій же послідовності, в якій вони були відправлені, і без втрат. TCP встановлює з'єднання, передає дані і завершує з'єднання.

Потік даних (ще разок!) - абстракція, яка використовується для читання або запису файлів, сокетов і т.п. в єдиній манері. Потоки є зручним уніфікованим програмним інтерфейсом для читання або запису файлів (в тому числі спеціальних і, зокрема, пов'язаних з пристроями), сокетов і передачі даних між процесами.

Хост (господар, який приймає гостей або вузол мережі) - будь-який пристрій, що надає послуги формату "клієнт-сервер" в режимі сервера з яких-небудь інтерфейсів і унікально певне на цих інтерфейсах. У більш окремому випадку під хостом можуть розуміти будь-який комп'ютер, сервер, підключений до локальної або глобальної мережі. Хост - це будь-яка одиниця комп'ютерної техніки, яка підключена до комп'ютерної мережі.

Порт - в протоколах TCP і UDP (сімейства TCP / IP) порт являє собою ідентифікований номером системний ресурс, що виділяється з додатком, що виконується на деякому мережевому хості, для зв'язку з додатками, виконуваними на інших мережевих хостах (в тому числі c іншими додатками на цьому ж хості).

Для кожного з протоколів TCP і UDP стандарт визначає можливість одночасного виділення на хості до 65536 унікальних портів, що ідентифікуються номерами від 0 до 65535. При передачі по мережі номер порту в заголовку пакета використовується (разом з IP-адреси хоста) для адресації конкретного додатка (і конкретного, що належить йому, підключення до мережі).

У звичайній клієнт-серверної моделі один додаток очікує входять даних або запиту на з'єднання (сервер "слухає порт"), інше посилає дані або запит на з'єднання в відомий порт, відкритий додатком-сервером (клієнт).

За замовчуванням додатком видається порт з довільним (наприклад, найближчим вільним і великим тисячі двадцять чотири) номером. При необхідності додаток може запитати конкретний (зумовлений) номер порту. Так, веб-сервери зазвичай відкривають для очікування з'єднання зумовлений порт 80 протоколу TCP.

побутовий приклад : На вокзалі каса з трьома віконцями (для поїздів далекого прямування, ближнього і військових) і трьома касирами, кожен з яких обслуговує своє вікно. Касири п'ють каву або грають в карти, але коли до якогось вікна підійде пасажир, його обслужить відповідний касир. Вокзал - мережа, каса - хост, касири - сервери, вікна - порти, пасажири - клієнти.

Дуже добре питання мережевого програмування описані в книзі (скоріше, єдиною такою):

".NET - Мережеве програмування для професіоналів"

Автори: Ендрю Кровчік, Винод Кумар, Номан Лагарі, Аджіт Мунгале, Крістіан Нагель, Тім Паркер,

Шрініваса Шівакумар

Видавництво: Лорі

Рік: 2005

Приклад 1. Створення простої тестової Служби Windows

Сервером в C # може служити додаток будь-якого з існуючих здійсненних типів: Console Application, Windows Forms Application, WPF Application або Windows Service. Для різноманітності, спочатку побудуємо сервер на основі Windows Service. Такі програми не мають призначеного для користувача інтерфейсу (він їм і не потрібен - висять в оперативній пам'яті і роблять свою справу) і раніше називалися резидентними, а тепер Службами.

Пізніше ми створимо сервер і як Console Application, в якому консольне вікно може бути використано для запуску, налаштувань і контролю стану сервера. А поки трохи відвернемося і на простому прикладі навчимося створювати Службу Windows як прототип нашого майбутнього сервера, потім знову продовжимо розгляд теми потоків.

Оболонка створить новий проект і покаже вікно графічного конструктора сервісів.

using System; using System.ServiceProcess; using System.Timers; using System.IO; namespace TestService {public partial class Service1: ServiceBase {Timer timer; // Системний таймер String logFile = "C: \\ Report.txt"; // Конструктор public Service1 () {// Створюємо та налаштовуємо системний таймер timer = new Timer (); timer.Elapsed + = timer_Elapsed; // Реєструємо обробник timer.Interval = 5000; // 5 секунд} // Обробник події таймера void timer_Elapsed (object sender, ElapsedEventArgs e) {StreamWriter writer = File.AppendText (logFile); writer.WriteLine (DateTime.Now.ToString ()); writer.Close (); // Закриваємо потік} // Метод спрацьовує при запуску Служби protected override void OnStart (string [] args) {timer.Start (); } // Метод спрацьовує при зупинці Служби protected override void OnStop () {timer.Stop (); }}}

Запускати на виконання через оболонку цю Службу не можна. Вона запускається операційною системою через Диспетчер Служб (або через Диспетчер завдань Windows по жесту Ctrl-Alt-Del). Ми її можемо поки тільки відкомпілювати, але і це передчасно. Ще потрібно зареєструвати Службу на поточному комп'ютері, щоб вона була видна диспетчеру Служб, а для цього потрібно до проекту додати клас налаштувань реєстрації Служби.

using System; using System.ComponentModel; using System.Configuration.Install; using System.ServiceProcess; namespace TestService {[RunInstaller (true)] // Під час установки збірки слід викликати установник public partial class Installer1: Installer {private ServiceInstaller serviceInstaller; private ServiceProcessInstaller serviceProcessInstaller; public Installer1 () {// Створюємо об'єкти налаштування для Служби serviceInstaller = new ServiceInstaller (); serviceProcessInstaller = new ServiceProcessInstaller (); // Ім'я Служби для машини і користувача serviceInstaller.ServiceName = "TestServiceServiceName"; serviceInstaller.DisplayName = "TestServiceDisplayName"; // Контекст безпеки - як буде запускатися Служба this.serviceProcessInstaller.Account = ServiceAccount.LocalSystem; this.serviceProcessInstaller.Password = null; this.serviceProcessInstaller.Username = null; // Додаємо настройки в колекцію поточного об'єкта this.Installers.AddRange (new Installer [] {serviceInstaller, serviceProcessInstaller}); }}}

Ми створили Службу, яка повинна бути зареєстрована в операційній системі. Як це робиться, описано в MSDN ( ' http://msdn.microsoft.com/ru-ru/library/sd8zc8ha(VS.90).aspx ') І ми це зараз виконаємо. Для реєстрації Служб додатків .NET використовується утиліта InstallUtil.exe, яка знаходиться в каталозі "C: \ Windows \ Microsoft.NET \ Framework \ v2.0.50727". Для видалення реєстрації Служби використовується та ж сама утиліта, але з опцією / u.

зауваження

  1. Викликати вікно зі списком Служб можна і через Диспетчер завдань Windows комбінацією клавіш Ctrl-Alt-Del (колись заповітна комбінація для програмістів DOS!)
  2. Створена нами Служба запускається вручну, але через команду Властивості контекстного меню її можна зробити запускається автоматично при завантаженні комп'ютера
  3. Після реєстрації Служби в системі не можна міняти місце розташування створеної нами збірки, інакше при запуску система цю Службу просто не знайде. При необхідності Службу потрібно спочатку разрегістріровать, перенести і знову зареєструвати для нового місця
  4. В каталозі збірки утиліта інсталяції InstallUtil.exe створила два протокольних текстові файли (без розширення): TestService.InstallLog і TestService.InstallState, які не впливають на подальшу роботу Служби і їх можна спокійно видалити

Отже, на цьому простому прикладі ми навчилися створювати Служби Windows (не плутайте з Web-служби) і тепер можна б приступити до створення серверної програми, яка за запитом клієнтів буде отримувати малюнки з БД і посилати їм (їх) для перегляду. Але не будемо поспішати і розглянемо ще один тестовий приклад, тепер вже ілюструє взаємодію клієнта і сервера.

CommandText = "SELECT Picture FROM MyTable WHERE FileName =?