НОУ ІНТУЇТ | лекція | Робота з потоками даних
- Приклад 7. Використання бази даних з малюнками
- Завдання до вправи
- Вправа 8. Мережеві додатки на базі потоку NetworkStream
- Приклад 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.
зауваження
- Викликати вікно зі списком Служб можна і через Диспетчер завдань Windows комбінацією клавіш Ctrl-Alt-Del (колись заповітна комбінація для програмістів DOS!)
- Створена нами Служба запускається вручну, але через команду Властивості контекстного меню її можна зробити запускається автоматично при завантаженні комп'ютера
- Після реєстрації Служби в системі не можна міняти місце розташування створеної нами збірки, інакше при запуску система цю Службу просто не знайде. При необхідності Службу потрібно спочатку разрегістріровать, перенести і знову зареєструвати для нового місця
- В каталозі збірки утиліта інсталяції InstallUtil.exe створила два протокольних текстові файли (без розширення): TestService.InstallLog і TestService.InstallState, які не впливають на подальшу роботу Служби і їх можна спокійно видалити
Отже, на цьому простому прикладі ми навчилися створювати Служби Windows (не плутайте з Web-служби) і тепер можна б приступити до створення серверної програми, яка за запитом клієнтів буде отримувати малюнки з БД і посилати їм (їх) для перегляду. Але не будемо поспішати і розглянемо ще один тестовий приклад, тепер вже ілюструє взаємодію клієнта і сервера.
CommandText = "SELECT Picture FROM MyTable WHERE FileName =?