Eötvös Loránd Tudományegyetem Informatikai Kar
Webes alkalmazások fejlesztése 2. előadás Kliens-szerver rendszerek megvalósítása (TCP/IP)
© 2014.02.17. Giachetta Roberto
[email protected] http://people.inf.elte.hu/groberto
Kliens-szerver rendszerek megvalósítása Hálózati kapcsolatok
• Alkalmazások hálózaton át történő kommunikációjának két módja van: • kapcsolat-orientált: a kapcsolatban két végpont, egy kliens és egy szerver vesz részt, amelyek között a kapcsolat kölcsönös elfogadáson („kézrázáson”) alapul • erre szolgál a Transfer Control Protocol (TCP) • biztonságos (az adatok célba érnek), de lassú • kapcsolat-mentes: a két végpontnak nincs tudomása egymásról • erre szolgál a User Datagram Protocol (UDP) • gyors, de nem biztonságos ELTE IK, Webes alkalmazások fejlesztése
2:2
Kliens-szerver rendszerek megvalósítása Hálózati kapcsolatok
• A .NET keretrendszerben a hálózati kommunikációt biztosító osztályok a System.Net névtérben helyezkednek el • A keretrendszer két absztrakciós réteget is biztosít: • a magasabb szintű absztrakciós rétegen a protokollnak megfelelő osztályok foglalnak helyet (TcpClient, TcpListener, UdpClient), amelyek lehetőséget adni a hálózati kapcsolat folyam-alapú kezelésére (NetworkStream) • az alacsonyabb szintű rétegen a protokoll-független kapcsolati osztály (Socket) segítségével tudunk direkt kapcsolatot létesíteni ELTE IK, Webes alkalmazások fejlesztése
2:3
Kliens-szerver rendszerek megvalósítása TCP kommunikáció
• A TCP kliens objektum (TcpClient) a klienskezeléshez használható a kliens és a szerver oldalon is • a Connect metódussal csatlakozhatunk egy megadott hálózati pontra (IP cím, port), a kapcsolatot a Close metódussal zárjuk • a kapcsolat állapotát a Connected tulajdonsággal ellenőrizhetjük • Pl.: TcpClient cl = new TcpClient(); cl.Connect(IPAddress.Parse("192.168.0.2"), 6500); // kapcsolódás if (cl.Connected) { … } // ha sikerült ELTE IK, Webes alkalmazások fejlesztése
2:4
Kliens-szerver rendszerek megvalósítása TCP kommunikáció
• A TCP szerver objektum (TcpListener) a szerveren figyeli a csatlakozási kérelmeket • példányosításkor megadjuk, mely portot figyelje, és mely cím(ek)ről fogadja a kéréseket (ha bármely címről fogadunk kéréseket, használhatjuk az IPAddress.Any értéket) • a szerver a Start művelettel indítható és a Stop metódussal állítható le • a szerver fogadhat csatlakozásokat az AcceptTcpClient metódussal, amely visszaadja a kliens példányt, és onnantól azon keresztül kommunikálhatunk a klienssel
ELTE IK, Webes alkalmazások fejlesztése
2:5
Kliens-szerver rendszerek megvalósítása TCP kommunikáció
• a csatlakozó távoli végpont információi elérhetőek a Client.RemoteEndPoint tulajdonságon keresztül • Pl.: TcpListener ls = new TcpListener(IPAddress.Any, 6500); // hallgatás a 6500-as porton, bármely címre ls.Start(); TcpClient client = ls.AcceptTcpClient(); // fogadunk egy csatlakozást // client.Client.RemoteAddress megadja a távoli // végpontot … ls.Stop(); // szerver leállítása ELTE IK, Webes alkalmazások fejlesztése
2:6
Kliens-szerver rendszerek megvalósítása TCP kommunikáció
• A kapcsolattal létrejön egy hálózati adatfolyam (NetworkStream), amelyet a kliens GetStream műveletével kérhetünk le • az adatfolyamra felváltva írhatunk, illetve olvashatunk • bináris tartalomhoz használhatjuk az író/olvasó (Read, Write) műveleteket • szöveges tartalomhoz használhatjuk az adatfolyam író/olvasó típusokat (StreamReader, StreamWriter), ahol a megszokott műveletekkel írhatunk/olvashatunk tartalmat (Read, ReadLine, Write, …) • a tartalom nem kerül automatikusan elküldésre, csak a Flush utasítás kiadása után ELTE IK, Webes alkalmazások fejlesztése
2:7
Kliens-szerver rendszerek megvalósítása TCP kommunikáció
• Pl.: NetworkStream stream = cl.GetStream(); // hálózati folyam lekérdezése StreamWriter writer = new StreamWriter(stream); // adatfolyam író példányosítása writer.WriteLine("Hello Network!"); // üzenet írása writer.Flush(); // üzenet küldése StreamReader reader = new StreamReader(stream); // adatfolyam olvasó példányosítása String answer = Reader.ReadLine(); // válasz kiolvasása ELTE IK, Webes alkalmazások fejlesztése
2:8
Kliens-szerver rendszerek megvalósítása TCP kommunikáció
szerver
kliens Connect TcpClient
TcpListener AcceptTcpClient
GetStream TcpClient
NetworkStream
StreamReader
ELTE IK, Webes alkalmazások fejlesztése
StreamWriter
2:9
Kliens-szerver rendszerek megvalósítása Példa
Feladat: Készítsünk egy egyszerű visszhangot biztosító kliensszerver rendszert. • a szerver konzol felületű alkalmazás, amely várja a csatlakozásokat, minden fogadott üzenetet kiír, majd visszaküldi a kliensnek (a szerver minden lecsatlakozás után megkérdezi, hogy leállítjuk-e) • a kliens szintén konzol felületű alkalmazás, amelyben üzeneteket küldhetünk a szervernek, és kiírja a képernyőre a szervertől kapott visszhangot (a kliensből nem lehet kilépni, folyamatosan várja a bemenetet) • mindkét esetben egy-egy objektum felel a tevékenységért (Client és Server), amelyeket a Run metódussal futtatunk ELTE IK, Webes alkalmazások fejlesztése
2:10
Kliens-szerver rendszerek megvalósítása Példa
Megvalósítás (Server.cs): try { while (true){ String message = reader.ReadLine(); Console.WriteLine("Fogadott üzenet: " + message); writer.WriteLine(message); writer.Flush(); // üzenet elküldése } } catch { // sikertelen olvasás, ekkor megszakadt // az adatfolyam … client.Close(); // kapcsolat bezárása } ELTE IK, Webes alkalmazások fejlesztése
2:11
Kliens-szerver rendszerek megvalósítása Tervezési szempontok
• A kliens-szerver architektúra fontos szerepet játszik olyan esetekben, amennyiben • egyes gépek között kommunikációt akarunk megvalósítani, • globális erőforrásokat (pl. adatbázis) használnánk • erőforrás-igényes műveleteket központilag hajtanánk végre • Kliens tervezési szempontjai: • üzemmódok: online, offline • csatlakozás módja: automatikus, manuális • kritikus tényezők: szerver nem elérhetőségének, leállásának, túlterheltségének kezelése ELTE IK, Webes alkalmazások fejlesztése
2:12
Kliens-szerver rendszerek megvalósítása Tervezési szempontok
• A szerver tervezési szempontjai: • működés: aktív (maga is kezdeményez kommunikációt a kliensekkel), passzív (csupán a kliensek kéréseit dolgozza fel, majd válaszol) • felhasználói felület: konzol, grafikus, vagy nincs • kezelés: automatikus, manuális • kritikus tényezők: teherbírás, rendelkezésre állás • közös erőforráshoz való hozzáférés (párhuzamosítás miatt kölcsönös kizárást igényel) • eseménynaplózás (pl. adatbázisban, vagy rendszer eseménynaplóban) ELTE IK, Webes alkalmazások fejlesztése
2:13
Kliens-szerver rendszerek megvalósítása Tervezési szempontok
• Egy kliens-szerver rendszer esetén elkerülhetetlen a párhuzamosítás • a szervernek egyszerre több klienst is ki kell szolgálnia • a kliens nem blokkolódhat a kommunikáció során • A párhuzamosításért a System.Threading névtér típusai használhatóak, alapvetően két lehetőségünk van: • a szál (Thread) alacsony szinten biztosítja egy adott tevékenység (metódus) párhuzamos futtatását • a taszk (Task) egy magasabb szintű megoldás, amely egy tevékenység jövőbeli végrehajtásának ütemezését és futtatását biztosítja ELTE IK, Webes alkalmazások fejlesztése
2:14
Kliens-szerver rendszerek megvalósítása Párhuzamosítás
• Szálak esetén egy metódust adunk meg, amelyet a szál lefuttat • a futtatást a Start művelettel érjük el • a megszakítást az Abort metódussal kezdeményezhetjük, ám ez nem terminálja a szálat, hanem kivételt (ThreadAbortException) vált ki • állapota az IsAlive és ThreadState tulajdonságokkal kérhetjük le • lehetőségünk várakozni a szál lefutására a Join metódussal • bármely szál szüneteltethető a Thread.Sleep(
) metódussal
ELTE IK, Webes alkalmazások fejlesztése
2:15
Kliens-szerver rendszerek megvalósítása Párhuzamosítás
• Pl.: private void Process(){ // futtatandó tevékenység … Thread.Sleep(1000); // altatás 1 másodpercre … } … Thread myThread = new Thread(new ThreadStart(Process)); // szál példányosítása a megadott tevékenységre myThread.Start(); // szál indítása … myThread.Join(); // várakozás a szál lefutására
ELTE IK, Webes alkalmazások fejlesztése
2:16
Kliens-szerver rendszerek megvalósítása Párhuzamosítás
• A taszkok lambda-kifejezésként megadott tevékenységet (Action), vagy függvényt (Func) tudnak végrehajtani • függvény esetén a taszknak ismernie kell az eredmény típusát, ezt sablonként adjuk meg • a taszk futtatható egy lépésben (Task.Run), vagy létrehozható, és később elindítható (Start), vagy használható egy külön gyártó objektum (Task.Factory.StartNew) • kezelhető az állapota (Status, IsCanceled, IsCompleted) • a taszk eredményét a Result tulajdonságtól kérhetjük le (ekkor a program várakozik az eredmény megszületésére) ELTE IK, Webes alkalmazások fejlesztése
2:17
Kliens-szerver rendszerek megvalósítása Párhuzamosítás
• Pl.: private void Process(){ … } // futtatandó tevékenység private void RunProcess() { Task.Run(() => Process()); // a tevékenység lambda-kifejezését adjuk meg, // és taszkban futtatjuk … }
ELTE IK, Webes alkalmazások fejlesztése
2:18
Kliens-szerver rendszerek megvalósítása Párhuzamosítás
• Pl.: private Int32 Process(){ … } // eredményt adó tevékenység private void RunProcess() { // aszinkron művelet Int32 result = Task.Run(() => Process()).Result; // taszk lefuttatása és az eredmény bevárása … }
ELTE IK, Webes alkalmazások fejlesztése
2:19
Kliens-szerver rendszerek megvalósítása Párhuzamosítás
• Pl.: private Int32 Process(){ … } // eredményt adó tevékenység private void RunProcess() { Task myTask = new Task(() => Process()); // taszk legyártása tevékenység megadással myTask.Start(); // taszk indítása … Int32 result = myTask.Result; // az eredmény bevárása … } ELTE IK, Webes alkalmazások fejlesztése
2:20
Kliens-szerver rendszerek megvalósítása Példa
Feladat: Módosítsuk az előző alkalmazást úgy, hogy párhuzamosan tudja fogadni a klienseket. • a szervert alakítjuk át úgy, hogy minden klienskommunikáció kezelése külön taszkba kerüljön • magát a hallgatózást is áthelyezhetjük egy taszkba, így a szerver nem kérdezi meg minden kliens csatlakozása esetén, hogy ki szeretnénk-e lépni, hanem mi döntjük el, mikor szakítjuk meg a futtatást • ügyelni kell arra, hogy hallgatózás közben a TcpListener leállítása SocketException kivételt okoz
ELTE IK, Webes alkalmazások fejlesztése
2:21
Kliens-szerver rendszerek megvalósítása Példa
Megvalósítás (Server.cs): public void Run(){ … Task.Run(() => { // új taszk a szerver futtatásához try { while (true) { … } } catch (SocketException) { } // ha menet közben leállítjuk a szervert Console.WriteLine("Szerver leállítva."); });
ELTE IK, Webes alkalmazások fejlesztése
2:22
Kliens-szerver rendszerek megvalósítása Példa
Megvalósítás (Server.cs): Console.WriteLine("Kilépés billentyűleütésre."); // kilépés Console.ReadKey(); _TcpListener.Stop(); // szerver leállítása } … private void HandleCommunication(TcpClient client){ Task.Run(() => { // új taszk a klienssel való kommunikációhoz … }); } ELTE IK, Webes alkalmazások fejlesztése
2:23
Kliens-szerver rendszerek megvalósítása Párhuzamosítás
• A párhuzamosítás elérhető aszinkron (async) tevékenységek használatával is • a taszkok futása bevárható az await kulcsszóval, pl.: private async void RunProcess() { // aszinkron művelet Int32 result = await Task.Run(() => Process()); // az eredmény bevárása … }
• számos beépített metódus rendelkezik aszinkron változattal (pl. ConnectAsync, ReadLineAsync), amelyek szintén bevárhatóak ELTE IK, Webes alkalmazások fejlesztése
2:24
Kliens-szerver rendszerek megvalósítása Párhuzamosítás grafikus felületen
• Amennyiben az alkalmazás párhuzamos szálon is futtat tevékenységet, ügyelni kell arra, hogy az nem férhet hozzá a grafikus felülethez, csak szinkronizálást követően: • szál (Thread) és aszinkron művelet esetén a Dispatcher osztály biztosítja a szinkronizációt, pl.: Dispatcher.CurrentDispatcher.BeginInvoke( new Action(() => { … }));
• taszk esetén futtathatjuk a teljes tevékenységet az előre megadott szinkronizációs kontextusban, ami így a felület szálán fut végrehajtódni • más tekintetben is paraméterezhető a taszk (pl. megszakítás engedélyezése) ELTE IK, Webes alkalmazások fejlesztése
2:25
Kliens-szerver rendszerek megvalósítása Taszkok paraméterezése
• A taszk megszakítását a CancellationToken biztosítja • a token a CancellationTokenSource típusból kérhető le, majd a Cancel() metódussal kezdeményezhető a leállítás • ez nem szakítja meg automatikusan a működést, csak egy jelzést ad • az IsCancellationRequested tulajdonsággal kérhetjük le az állapotot, és zárhatjuk le a taszk működését • A taszk viselkedését a TaskCreationOptions felsorolási típus adja meg, pl. megadhatunk pártatlan ütemezésre törekvést (PrefairFairness), illetve jelezhetjük, hogy hosszan futó taszkról van szó (LongRunning) ELTE IK, Webes alkalmazások fejlesztése
2:26
Kliens-szerver rendszerek megvalósítása Taszkok paraméterezése
• Pl.: CancellationTokenSource source = new CancellationTokenSource(); // tokenforrás létrehozása Task.Run(() => { … if (source.IsCancellationRequested) // ha kérték a megszakítást return; // megszakítjuk a futást … }, source.Token); // megadjuk a megszakító tokent
ELTE IK, Webes alkalmazások fejlesztése
2:27
Kliens-szerver rendszerek megvalósítása Taszkok paraméterezése
• Taszkok szinkronizálását a TaskScheduler típus biztosítja • a statikus FromCurrentSynchronizationContext() metódus az aktuális szálba történő szinkronizálást fogja biztosítani • paraméterként megadhatjuk a taszknak • Általában nem a tevékenységet szeretnénk szinkronizálni, hanem a felülethez történő hozzáférést, ehhez: • futtathatunk egy belső, szinkron taszkot • a taszk befejezte után lefuttathatjuk még egy tevékenységet, ehhez a ContinueWith(…) metódust használhatjuk, amely sorosítja a taszkok működését ELTE IK, Webes alkalmazások fejlesztése
2:28
Kliens-szerver rendszerek megvalósítása Taszkok paraméterezése
• Pl.: TaskScheduler scheduler = TaskScheduler.FromCurrentSynchronizationContext(); // a visszaszinkronizálás biztosítása Task.Factory.StartNew(() => { … }, …, …, scheduler) // a taszk szinkron fog futni Task.Factory.StartNew(() => { … }) .ContinueWith(() => { label.Text = "Ready." }, scheduler); // a taszk párhuzamosan fut, majd ha végez // lefuttat egy szinkron tevékenységet, így // biztonságosan írhatunk a felületre ELTE IK, Webes alkalmazások fejlesztése
2:29
Kliens-szerver rendszerek megvalósítása Példa
Feladat: Készítsünk egy kliens-szerver alapú csevegőszobát. • a kliens egy WPF grafikus felületű alkalmazás, ahol a felhasználó előbb csatlakozik a szerverhez az IP cím és felhasználónév megadásával, majd írhat a közös szobába • a kliens MVVM architektúrában építjük fel, a modell feladata a hálózatkezelés (NetworkManager), a bejövő üzeneteket eseménnyel továbbítja • a szerver konzol felületű alkalmazás, amely kezeli a klienseket, továbbítja az üzeneteket • az üzeneteket már a kliens szintjén összeállítjuk (felhasználónév, időpont, szöveg), egyszerű szövegként továbbítjuk ELTE IK, Webes alkalmazások fejlesztése
2:30
Kliens-szerver rendszerek megvalósítása Példa
Tervezés (ChatRoom.Client):
ELTE IK, Webes alkalmazások fejlesztése
2:31
Kliens-szerver rendszerek megvalósítása Példa
Tervezés (ChatRoom.Client): class Client ViewModelBase
Model::NetworkManager -
_client :TcpClient _reader :StreamReader _writer :StreamWriter
+ Close() :void + Connect(String, Int32) :void + NetworkManager() -_Manager NetworkManager_ConnectionChanged(object, EventArgs) :void OnConnectionChanged() :void OnMessageReceived(String) :void ReadMessages() :void + SendMessage(String) :void «event» + ConnectionChanged() :EventHandler + MessageReceived() :EventHandler<MessageEventArgs> «property» + IsConnected() :Boolean
ELTE IK, Webes alkalmazások fejlesztése
ViewModel::ChatRoomViewModel -
_connectCommand :DelegateCommand _manager :NetworkManager _sendCommand :DelegateCommand
+ ChatRoomViewModel() InsertMessage(String) :void Manager_ConnectionChanged(object, EventArgs) :void Manager_MessageReceived(object, MessageEventArgs) :void «property» + Address() :String + CanConnect() :Boolean + ConnectCommand() :ICommand + CurrentText() :String + IsConnected() :Boolean + Messages() :ObservableCollection<String> + SendCommand() :ICommand + UserName() :String
2:32
Kliens-szerver rendszerek megvalósítása Példa
Tervezés(ChatRoom.Server): class Server ChatServer -
_clients :List _isRunning :Boolean _listener :TcpListener _writers :List<StreamWriter>
+ + +
ChatServer() HandleClient(TcpClient) :void Start() :void Stop() :void
ELTE IK, Webes alkalmazások fejlesztése
Program -
Main(string[]) :void
2:33
Kliens-szerver rendszerek megvalósítása Példa
Megvalósítás (NetworkManager.cs): private void ReadMessages(){ TaskScheduler scheduler = …; // a visszaszinkronizáláshoz a jelenlegi // szálba Task.Factory.StartNew(() => { … }, TaskCreationOptions.LongRunning) .ContinueWith(_ => OnConnectionChanged(), scheduler); // megadjuk a később lefuttatandó // tevékenységet }
ELTE IK, Webes alkalmazások fejlesztése
2:34
Kliens-szerver rendszerek megvalósítása Az erőforrásokhoz való hozzáférés megvalósítása
• A szerveren a közös erőforrások (pl. adatbázis) kezelése párhuzamos környezetben kölcsönös kizárást követel, nehogy inkonzisztens állapotba kerüljön az erőforrás • Ennek több lehetősége is adott, pl.: • szemafor (Semaphor), amely adott programsorokat tud kölcsönösen kizártan futtatni • monitor (Monitor) használata, amely egy adott objektumhoz biztosít kölcsönösen kizárt hozzáférést (ugyanez rövidítve a lock kulcsszó) • szinkronizált művelet, amely a művelethez való párhuzamos hozzáférést zárja ki (MethodImplOptions.Synchronized) ELTE IK, Webes alkalmazások fejlesztése
2:35
Kliens-szerver rendszerek megvalósítása Az erőforrásokhoz való hozzáférés megvalósítása
• Pl.:
private Object resource; // erőforrás … lock(resource){ // kritikus szakasz az erőforrásra, amíg valaki // bent van a szakaszban ugyanezzel az // objektummal, más nem léphet be … } // kritikus szakasz vége
• Adatbázisokhoz való hozzáférés esetén maga az adatbázisszintű végrehajtás szálbiztos, de a programban való végrehajtás nem (így pl. ha minden szál külön entitásmodell példányt használ, nem kell kritikus szakasz) ELTE IK, Webes alkalmazások fejlesztése
2:36
Kliens-szerver rendszerek megvalósítása A kommunikációs csatorna
• A kliens és szerver közti kommunikációban byte sorozatokat közvetítünk, de magasabb szinten összetett adatokat közlünk • magának az üzenetnek is többnyire van típusa (pl. bejelentkezés, játék indítása, …), amely az üzenetet nyitja és meghatározza további tartalmat kliens
üzenettípus
tartalom
szerver
• a .NET keretrendszerben biztosított a hálózati kapcsolat csatorna alapú kezelése (NetworkStream), amely lehetővé tesz szöveges adatközlést, alacsonyabb szinten csak Byte[] tartalom közölhető ELTE IK, Webes alkalmazások fejlesztése
2:37
Kliens-szerver rendszerek megvalósítása A kommunikációs csatorna
• A kommunikáció lehetséges megoldásai: • szöveges: • a tartalmat valamilyen elhatároló jellel (pl.: ;, |, $) választjuk el egymástól • általános, rövid, de összetett üzeneteknél nagyon bonyolult sorozatot eredményezhet • pl.: Text|User01|User10|Hello friend! • szérializált (objektumszérializáció alkalmazásával): • könnyen végrehajtható, de az osztályszerkezetet a kliensnek és a szervernek is ismernie kell
ELTE IK, Webes alkalmazások fejlesztése
2:38
Kliens-szerver rendszerek megvalósítása A kommunikációs csatorna
• XML alapú: • a tartalom objektumhierarchiának megfelelően, vagy akár tetszőlegesen felépíthető • testre szabható, könnyen olvasható, általános megoldás • pl.: <Message Type="Text" SenderName="User01" TargetUser="USer10"> Hello friend!
• XML-szérializációval könnyen végrehajtható, de az osztályoknak megegyező felépítéssel kell rendelkeznie a kliens és a szerver oldalon ELTE IK, Webes alkalmazások fejlesztése
2:39
Kliens-szerver rendszerek megvalósítása XML szérializáció
• A .NET keretrendszer több módszert is kínál XML adatok kezelésére, egyike az XML formátumú objektumszérializáció • az osztályokat, illetve tulajdonságokat attribútumokkal jelöljük meg, amely megadja XML-beli szerepüket (XmlRoot, XmlElement, XmlAttribute) • a típusok egymásba ágyazhatóak (XmlInclude(…) attribútum segítségével) • az XmlSerializer típus biztosítja a szérializációt egy megadott adatfolyamba • ha szeretnénk bináris formában megkapni az XML-t, akkor MemoryStream segítségével elvégezhető az átalakítás ELTE IK, Webes alkalmazások fejlesztése
2:40
Kliens-szerver rendszerek megvalósítása XML szérializáció
• Pl.: [XmlRoot] // XML gyökér elem class Message { [XmlAttribute] // XML attribútum public Int32 Code { get; set; } [XmlElement] // XML beágyazott elem public String Content { get; set; } }; // XMl szérializálható típus a megfelelő // attribútumokkal Message m = new Message { Code = 1, Content = "Hello World!" }; ELTE IK, Webes alkalmazások fejlesztése
2:41
Kliens-szerver rendszerek megvalósítása XML szérializáció
• Pl.: MemoryStream stream = new MemoryStream(); // memóriabeli adatfolyam XmlSerializer ser = new XmlSerializer(typeof(message)); // szérializáló a megadott típusra serializer.Serialize(stream, message); // szérializáció megadott adatfolyamba, a // keletkezett XML: // <Message Code = "1"> // Hello World! // Byte[] data = stream.ToArray(); // az XML bináris alakban ELTE IK, Webes alkalmazások fejlesztése
2:42
Kliens-szerver rendszerek megvalósítása Példa
Feladat: Módosítsuk az előző rendszert úgy, hogy lehessen privát üzeneteket is küldeni a felhasználóknak. • módosítjuk a klienst, megjelenik a privát küldés lehetősége, ehhez szükségünk van a felhasználók listájára (ebből kiválaszthatjuk a célszemélyt) • így már összetettebb az üzenet, ezért nem szimpla szöveget, hanem XML-t küldünk a hálózaton, szérializációt használva, ehhez egy közös osztálykönyvtárat hozunk létre (ChatRoom.Communication) • külön üzenetet biztosítunk a felhasználónév megadására, kijelentkezésre, valamint a felhasználók listájának elküldésére (minden változást követően) ELTE IK, Webes alkalmazások fejlesztése
2:43
Kliens-szerver rendszerek megvalósítása Példa
Tervezés: cmp ChatRoom
Client
Server
Communication
ELTE IK, Webes alkalmazások fejlesztése
2:44
Kliens-szerver rendszerek megvalósítása Példa
Tervezés: class ChatRoom Model::NetworkManager -
_client :TcpClient _stream :NetworkStream
+ Connect(String, Int32, String) :void + Logout() :void + NetworkManager() NetworkManager_ConnectionChanged(object, EventArgs) :void OnConnectionChanged() :void OnMessageReceived(String, String, Boolean) :void OnUserListReceived(String) :void ReadMessages() :void + SendMessage(String, String) :void + SendMessage(String, String, String) :void «event» + ConnectionChanged() :EventHandler + MessageReceived() :EventHandler<MessageEventArgs> + UserListReceived() :EventHandler<UserListEventArgs> «property» + IsConnected() :Boolean
ELTE IK, Webes alkalmazások fejlesztése
Server::ChatServer Communication::Message + Deserialize(Byte[], Int32) :Message + Serialize(Message) :Byte[] «property» + Code() :MessageCode + SenderName() :String + TargetName() :String + Text() :String
-
_clients :List _isRunning :Boolean _listener :TcpListener _lockedObject :Object
+ + +
ChatServer() HandleClient(ChatClient) :void RemoveClient(ChatClient) :void SendMessage(Message) :void SendUserList() :void Start() :void Stop() :void
+Code «enumeration» Communication:: MessageCode Login = 101 Logout = 102 Text = 103 List = 110
-_Clients 0..* Server::ChatClient «property» + Client() :TcpClient + Stream() :NetworkStream + UserName() :String
2:45
Kliens-szerver rendszerek megvalósítása Példa
Megvalósítás (Message.cs): [XmlRoot] public class Message { [XmlAttribute] public MessageCode Code { get; set; } // kód [XmlAttribute] public String SenderName { get; set; } // küldő … [XmlElement] public String Text { get; set; } // szöveg … }
ELTE IK, Webes alkalmazások fejlesztése
2:46
Kliens-szerver rendszerek megvalósítása Példa
Megvalósítás (Message.cs): public static Byte[] Serialize(Message message){ using (MemoryStream stream = new MemoryStream()) { // memória adatfolyam segítségével alakítjuk // át XmlSerializer serializer = new XmlSerializer(typeof(Message)); serializer.Serialize(stream, message); // szérializáljuk a tartalmat XML-é return stream.ToArray(); // kivesszük az adatokat byte-tömbként } … ELTE IK, Webes alkalmazások fejlesztése
2:47
Kliens-szerver rendszerek megvalósítása Példa
Megvalósítás (Server.cs): … TcpClient tcpClient = _Listener.AcceptTcpClient(); // várakozunk a kliensekre ChatClient chatClient = new ChatClient { Client = tcpClient, Stream = tcpClient.GetStream() }; // kliens felvétele lock (_LockedObject) { // a kliensek listájához mindig kritikus // szakaszban férünk hozzá _Clients.Add(chatClient); } … ELTE IK, Webes alkalmazások fejlesztése
2:48
Kliens-szerver rendszerek megvalósítása Eseménynaplózás megvalósítása
• Célszerű, hogy a szerver a rajta futó tevékenységeket naplózza (pl. biztonsági, vagy hibakeresési okokból) • A .NET alkalmazásoknak lehetőségük van a Windows eseménynaplóját használni az EventLog osztály segítségével • a CreateEventSource metódussal létrehozhatunk új eseménynaplót az alkalmazásnév (forrás) és a napló nevének megadásával (számos megkötés van az elnvezésekre) • a létezést ellenőrizhetjük a SourceExists metódussal • bejegyzést a WriteEntry metódussal írhatunk, a bejegyzésnek különböző típusai lehetnek (Information, Error, …) ELTE IK, Webes alkalmazások fejlesztése
2:49
Kliens-szerver rendszerek megvalósítása Eseménynaplózás megvalósítása
• az eseménynapló a Delete művelettel törölhető • az utasítások kivételt dobnak, ha az alkalmazásnak nincs joga hozzáférni az eseménynapló információkhoz • pl.: EventLog.CreateEventSource("MyServer", "MyServerLog"); // eseménynapló létrehozása … EventLog serverLog = new EventLog(); serverLog.Source = "MyServer"; // forrás serverLog.Log = "MyServerLog"; // név serverLog.WriteEntry("Szerver elindult.", EventLogEntryType.Information); // bejegyzés az eseménynaplóba ELTE IK, Webes alkalmazások fejlesztése
2:50
Kliens-szerver rendszerek megvalósítása Windows szolgáltatások
• Amennyiben a szerver automatikusan tevékenykedik, nem szükséges, hogy felhasználói felülettel rendelkezzen • Lehetőségünk van olyan alkalmazások készítésére, amelyek nem rendelkeznek felülettel, hanem a háttérben futnak, ezeket nevezzük szolgáltatásoknak (Windows Service) • a szolgáltatásokat a vezérlőpulton keresztül kezelhetjük • futásának módja direkt módon, vagy előre beállítottan kezelhető (pl. automatikus indítás/leállítás) • a szolgáltatás kommunikálhat más programokkal, adatbázisokkal, illetve használhatja a rendszer eseménynaplóját bejegyzések rögzítésére ELTE IK, Webes alkalmazások fejlesztése
2:51
Kliens-szerver rendszerek megvalósítása Windows szolgáltatások
• A szolgáltatásokat .NET-ben külön projekttípusban (Windows Service) hozhatjuk létre • a szolgáltatás ősosztálya a ServiceBase, ennek felül kell definiálnunk OnStart, OnStop metódusait, ezek megadják az indításnál/leállításnál végrehajtandó tevékenységeket • a szolgáltatást a ServiceBase osztály Run metódusával futtathatjuk, amelyet a főprogram végez • a szolgáltatást rendszerint egy ciklusban történő végrehajtás vezérli egy adott metódusban, amelyet kötelező külön szálban futtatni (mivel az indítás metódusának adott időn belül terminálnia kell, különben a rendszer megszakítja az indítást) ELTE IK, Webes alkalmazások fejlesztése
2:52
Kliens-szerver rendszerek megvalósítása Windows szolgáltatások
• Pl.: public partial class MyService : ServiceBase { … protected override void OnStart(string[] args){ … // indítási tevékenységek eventLog.WriteEntry("Szolgáltatás elindult.", EventLogEntryType.Information); // csak naplózással kommunikálunk } protected override void OnStop(){ … // leállítási tevékenységek } } ELTE IK, Webes alkalmazások fejlesztése
2:53
Kliens-szerver rendszerek megvalósítása Windows szolgáltatások telepítése
• A szolgáltatást az adott gépre telepítenünk kell • a telepítéshez szükségünk van egy Installer objektumra, amely bekonfigurálja a szolgáltatást (esetleg inicializálja az eseménynaplót) • két telepítőt kell felkonfigurálnunk: • ServiceProcessInstaller: megadja a kezelő felhasználó adatait • ServiceInstaller: megadja a szolgáltatás leírását, kezelésének módját (manuális, automatikus, …), függőségeit, … • az InstallUtil program segítségével telepíthetjük a szolgáltatást ELTE IK, Webes alkalmazások fejlesztése
2:54
Kliens-szerver rendszerek megvalósítása Windows szolgáltatások telepítése
• Pl.:
[RunInstaller(true)] // telepítőként való futtatás public partial class ServerInstaller : System.Configuration.Install.Installer { … public ServerInstaller() { _serviceProcessInstaller.Account = ServiceAccount.LocalService; // a szolgáltatás futtatója _serviceProcessInstaller.Password = null; _serviceProcessInstaller.Username = null; // a futtató azonosítója (mivel // rendszerfiók futtatja, ezért nem // szükségesek)
ELTE IK, Webes alkalmazások fejlesztése
2:55
Kliens-szerver rendszerek megvalósítása Windows szolgáltatások telepítése
• Pl.: _serviceInstaller.Description = "My Server"; // szolgáltatás leírása _serviceInstaller.ServiceName = "MyServer"; // szolgáltatás neve _serviceInstaller.StartType = ServiceStartMode.Automatic; // automatikus indulás a rendszerrel Installers.AddRange(new Installer[] { _serviceProcessInstaller, _serviceInstaller}); // telepítők felvétele … ELTE IK, Webes alkalmazások fejlesztése
2:56
Kliens-szerver rendszerek megvalósítása Telepítő projektek
• A telepítés kezdeményezhető az InstallUtil program segítségével, vagy telepítő projekt (Setup Project) létrehozásával Visual Studio-ban • a telepítőben megadhatjuk, mely projektek kimentét, függőségeit, valamint egyéb erőforrásait kívánjuk telepíteni • szerkeszthetjük a fájlrendszer (File System), regisztrációs adatbázis (Registry), valamint fájltársítási (File Types) műveleteket, definiálhatunk telepítési feltételeket (Launch Conditions), valamint egyedi telepítési műveleteket (Custom Actions) • szolgáltatás esetén az egyedi műveletek minden pontjához fel kell azt vennünk (a telepítő objektum futtatásához) ELTE IK, Webes alkalmazások fejlesztése
2:57
Kliens-szerver rendszerek megvalósítása Példa
Feladat: Módosítsuk az előző rendszert úgy, hogy a szerver, mint Windows szolgáltatás fusson a szerveren. • a szerver felépítése passzol a szolgáltatás működési elvéhez (indítani, illetve leállítani van lehetőségünk, a futás külön szálban történik), ezért a ServerService projektbe átemeljük a szervert • a szolgáltatásban eseménynaplózzuk az indítási, illetve leállítási folyamatokat, valamint elfogjuk az esetleges kivételeket • létrehozzuk a megfelelő telepítő objektumot, valamint telepítő projektet
ELTE IK, Webes alkalmazások fejlesztése
2:58