Programování služeb ve Windows - 4.díl
Začínáme doplňovat Class Server1
Doplníme konstanty a konstruktory :
public partial class Server1 : Component { private const string DISPLAYNAME = "Moje_Service_1"; private const string PATHFILE = @"c:\Temp\quotes.txt"; private const string THREADNAME = "Listener"; private const string MACHINENAME = "."; //1 private const string LOGNAME = "Application"; //2 private TcpListener listener = null ; private int port = 0 ; private string filename = String.Empty ; private List<string> quotes; private Random random; private Thread listenerThread = null ; private EventLog eventLog1 = new EventLog() ; private byte[] buffer; public Server1() : this (PATHFILE) //3 { } public Server1(string filename) : this(filename, 7890) //3 { } public Server1(string filename, int port) //3 { this.filename = filename; this.port = port; InitializeComponent(); Init(); eventLog1.WriteEntry("Konstruktor ", EventLogEntryType.Information); //0 } public void Start() //4 { ReadQuotes(); //5 eventLog1.WriteEntry("Start ", EventLogEntryType.Information); //0 - testovací smazat listenerThread = new Thread(new ThreadStart(ListenerThread)); //6 listenerThread.IsBackground = true; //7 listenerThread.Name = THREADNAME; //8 listenerThread.Start(); //9 } public void Stop() { listener.Stop(); } public void Suspend() { listener.Stop(); } public void Resume() { Start(); } public void RefreshQuotes() //10 { ReadQuotes(); //5 } protected void ReadQuotes() //11 { quotes = new List<string>(); Stream stream = File.OpenRead(filename); StreamReader streamReader = new StreamReader(stream); string quote; while ((quote = streamReader.ReadLine()) != null) { quotes.Add(quote); } streamReader.Close(); stream.Close(); random = new Random(); } protected string GetRandomQuoteOfTheDay() //12 { int index = random.Next(0, quotes.Count); return quotes[index]; } protected void ListenerThread() //13 { try { listener = new TcpListener(IPAddress.Loopback, port); //14 eventLog1.WriteEntry(IPAddress.Loopback.ToString() + " " + port.ToString(), EventLogEntryType.Information); //0 listener.Start(); //15 Socket clientSocket = null; while (true) //16 { if (!listener.Pending()) //17 { eventLog1.WriteEntry(" Není připojení ", EventLogEntryType.Warning); //0 } else { eventLog1.WriteEntry(" Je připijení ", EventLogEntryType.Information); //0 clientSocket = listener.AcceptSocket(); //18 string message = GetRandomQuoteOfTheDay(); //19 UnicodeEncoding encoder = new UnicodeEncoding(); buffer = encoder.GetBytes(message); clientSocket.Send(buffer, buffer.Length, 0); //20 clientSocket.Close(); //21 } Thread.Sleep(1000); //22 //performanceCounterRequestsTotal.Increment(); //performanceCounterBytesSentTotal.IncrementBy(buffer.Length); //requestsPerSec++; //bytesPerSec += buffer.Length; } } catch (SocketException ex) { string message = "Server selhal ve vlákně Listener: " + ex.Message; eventLog1.WriteEntry(message, EventLogEntryType.Error); } } [HostProtectionAttribute(SecurityAction.LinkDemand, Synchronization = true)] //23 private void Init() { eventLog1.Log = LOGNAME; eventLog1.MachineName = MACHINENAME; eventLog1.Source = DISPLAYNAME; EventLogTraceListener traceListener = new EventLogTraceListener(eventLog1); Trace.Listeners.Add(traceListener); }
Popis
- lokální PC
- získá nebo nastaví název protokolu, kde bude číst nebo zapisovat.
- konstruktory
- start serveru
- načte obsah souboru do kolekce List<string>
- příprava nového vlákna
- na pozadí
- jméno vlákna
- start vlákna
- nové volání pokud je služba úplná, bude vysvětleno později
- načte do quotes[i] obsah souboru
- vrací náhodný text i-tého řadku souboru
- výkonná část serveru
- inicializuje novou instanci TcpListener (127.0.0.1, port)
- začne zpracovávat příchozí požadavky na připojení
- nekonečná smyčka
- určuje, zda jsou požadavky na připojení, čeká na vyřízení, pokud není klient připojen -> false
- přijímá žádosti o připojení, čeká na vyřízení
- čte náhodný řádek textu souboru
- odešle řádek textu na klienta
- zavře Socket připojení a uvolní přiřazené zdroje
- čeká 1 sekundu, později mohu čas zmenšit
- inicializace logu a trastování
Testujeme
- otevřeme složku WindowsService1 - bin - Instalacni
- spustíme cmd.exe v režimu administrátora
- zadáme příkaz Uninstall.bat a pak Install.bat
- pokud instalace služby proběhne bez závad, zobrazíme si okno služeb
- spustíme naši službu na 2-5 sekund, pak ji zastavíme
- nebudeme provádět odinstalování služby!! (to znamená Uninstall.bat)
Naši službu jsme tímto zaregistrovali v databázi služeb a provedli zápis do registru.
Nastavíme TestServeru, MujServerEventListener a ClientServer jako exe projekty. Provedeme kompilaci celého našeho projektu.
Přetáhneme ze složky debug těchto projektů spouštěcí soubory na plochu jako zástupce. Spustíme ClientServer, MujServerEventListener a nakonec TestServeru. Pokud vše funguje, uvidíte:

Ve všech projektech můžete ladit, krokovat a provádět běžné činnosti Visual Studia.
Vytváření čítačů pro sledování výkonu
Novou kategorii čítačů pro naši službu vytvoříme v průzkumníku serverů, v aplikaci Visual Studio.

Po vyplnění přihlašovací tabulky, pokud nejsme administrator, VS naši aplikaci uloží, automaticky se přihlásí jako administrátor Visual Studia a znovu otevře naši aplikaci.

Pravým tlačítkem myši na čítačích výkonu zadáme novou kategorii a zobrazí se tabulka.

Tabulku vyplníme podle našich požadavků.
Přidávání komponent PerformanceCounter do služby
Můžeme postupovat buď přetažením komponenty nebo přidávat z panelu nástrojů. Zase nejjednodušší způsob je přetáhnout komponentu z průzkumníka serverů. Tímto způsobem dojde k automatickému nakonfigurování nových instancí:
- jméno PC (MachineName)
- název kategorie (CategoryName)
- vlastnost názvu (CounterName)
Nastavíme performanceCounter.ReadOnly = false, v této aplikaci služby nebudeme čítače výkonu číst, ale pouze zapisovat.
Výkonostní čítače, které ukazují celkové hodnoty, zvyšujeme přímo v metodě ListenerThread() třídy MujServer (viz ukázka kódu) . Pro počítání celkového počtu požadavků použijeme metodu performanceCounter.Increment() a výpočet odeslaných bajtů lze provést voláním metody performanceCounter.IncrementBy()

Omlouvám se, obrázky jsou z jiné služby a již jsem je nepředělával.
Class Server1 - doplnění kódu
public partial class Server1 : Component { //původní kód private byte[] buffer; private int bytesPerSec = 0; private int requestsPerSec = 0; protected void ListenerThread() { //původní kód if (!listener.Pending()) { Thread.Sleep(500); } else { //původní kód //čítače výkonu this.performanceCounter1.Increment(); requestsPerSec++; bytesPerSec += buffer.Length; } private void Init() { //přidáme this.performanceCounter1.ReadOnly = false; this.performanceCounter2.ReadOnly = false; this.performanceCounter3.ReadOnly = false; } private void timer1_Tick(object sender, EventArgs e) { this.performanceCounter2.RawValue = requestsPerSec; this.performanceCounter3.RawValue = bytesPerSec; bytesPerSec = 0 ; requestsPerSec = 0; } }
Aby vše fungovalo tak jak má, musíme přetáhnout na plochu čítač (Timer1), délka intervalu 1000 mS a trvale spouštěný. Myslím, že tato část nepotřebuje komentář. Potíž může nastat v nevhodně zvoleném typu čítače výkonu, zde doplňuji článkem z CodeProject:
Řešení není úplné, po restartu nebo odinstalování se z průzkumníka severu ztratí naše aplikace z čítačů výkonu. Pokud opravdu čítače výkonu potřebujete, musíte vytvořit kolekci, v tomto případě MojeService1. Jakým způsobem to provedete je rozebráno v uvedeném článku. Pokládám tento popis za zbytečný, článek by se rozšířil o dva další díly. Čítače výkonu musí být taktéž registrovány v registru.
Budeme pokračovat v dalším dílu.
Stáhnout
Stažením následujícího souboru souhlasíš s licenčními podmínkami
Staženo 77x (326.73 kB)
Aplikace je včetně zdrojových kódů v jazyce C#