• Nem Talált Eredményt

3. Többszálú szerver

3.6. Bináris Stream

public string ReadLine(StreamReader r, int timeoutMSec) { frameworkbeli ReadLine nem tartalmazza ezt az opciót.

3.6. Bináris Stream

A korábban bemutatott StreamReader és StreamWriter igazából string alapú kommunikációt jelent. Elsősorban a ReadLine és WriteLine metódusokkal tudunk adatokat fogadni és küldeni. Ez azt is jelenti, hogy minden adatot (számok) szöveges formára kell alakítani a küldéshez, a fogadás során pedig azokat vissza kell alakítani bináris formájukra. A korábban bemutatott mintában is felbukkan ez a megoldás, hiszen az összeadás során küldött adatokat a WriteLine által készített stringbe beillesztettük, a fogadó oldalon pedig az int.Parse segítségével visszaalakítottuk int típusúvá.

Minden string alakra hozás, parse művelet plusz processzoridőt köt le, lassítja a működést. Nagy mennyiségű adatok forgalmazásakor ez már jelentős idő, érdemes más megoldást keresni.

Egy másik stream párost kell ilyenkor alkalmazni. A BinaryWriter és BinaryReader osztályok példányai alapvetően ugyanúgy működnek, mint az eddig bemutatottak. A különbség abban rejlik, hogy ezek a streamek az adatokat lényeges átalakítás nélkül, a memóriában megadott byte-sorozat formájában (bináris alakban) továbbítják, illetve a fogadó oldalon is a beolvasott byte-sorozatot különösebb feldolgozás nélkül, közvetlenül tehetjük a memóriába. Emiatt nagy mennyiségű adatok küldése- és fogadása esetén a járulékos sebesség növelhető.

A BinaryWriter példány esetén a kódlap ugyanúgy megadható, mint a StreamWriter esetén. Az igazi különbségek tehát nem a példányosításnál, hanem a használatnál kerülnek majd elő.

BinaryReader r = new BinaryReader( bejovo.GetStream() );

BinaryWriter w = new BinaryWriter(bejovo.GetStream(), Encoding.UTF8);

Az író streamre a többszörösen túltöltött (overloaded) Write metódussal írunk. A Write 18 különböző paramétertípust tud kezelni, mindegyiket a maga bináris formájában írja ki. Ebben benne van a 8 egész típus, a 2 lebegőpontos, a logikai, karakter, string és néhány vektor típus is. A kiírással tehát nincs gond.

int i = 13; w.Write(i);

double d = 14.15; w.Write(d);

char c = ’A’; w.Write(c);

Az adat olvasását a bejövő stream számtalan ReadXXX metódusa végzi, ahol XXX helyébe az adott típus nevét kell helyettesíteni: az r.ReadBoolean() például egyetlen logikai érték beolvasását végzi el. A fenti 3 kiírás során kiírt adatokat az alábbi módon lehet beolvasni:

int i = r.ReadInt32();

double d = r.ReadDouble();

char c = r.ReadChar();

A továbbiakban minden, a korábbiakban említett probléma és lehetséges megoldása itt is fennáll. Az író streamre itt is alkalmazni kell a Flush-t, az olvasáshoz a timeout kezelése alapvetően itt sincs megoldva, de külön szálon indított olvasással kezelhető.

4. Összefoglalás

Az eddig megismert technikák, a szálkezelés (indítás, leállítás, állapot ellenőrzése), a védett blokkok kialakítása (lock, monitor osztály metódusai), az alacsony szintű stream alapú kommunikáció során igazából minden olyan eszközzel megismerkedtünk, amellyel az elosztott programozás alapszintű problémáit kezelni tudjuk. Mint láthattuk, képesek lehetünk olyan szerver alkalmazások fejlesztésére, amely egy időben több kliens programmal is képes kommunikálni, adatokat, feladatokat cserélni, időtúllépést (timeout) kezelni.

Ezen eszközök alkalmazása az elosztott programok fejlesztésére azonban olyan, mintha a programozók assemblyben fejlesztenének. A lehetőségeink szinte korlátlanok, csak programozási tudásunk és türelmünk jelölheti ki az általunk fejlesztett programok korlátait.

Az elkövetkezendőkben továbblépünk. Magasabb absztrakciós szintű módszerekkel fejlesztjük alkalmazásainkat. Azt fogjuk találni, hogy bizonyos dolgokkal így nem kell annyit foglalkoznunk, azokat a rendszer, egyfajta működtető motor szolgáltatni fogja. Jobban tudunk koncentrálni azoknak a programozási feladatoknak a megoldására, amelyek az adott alkalmazás sajátjai. Ugyanakkor minél magasabb szintre merészkedünk, annál kevésbé tudjuk befolyásolni a motor, a háttérbeli automatizmus működését. Ennek kapcsán néha kompromisszumokat is kell kötnünk.

Az előnyök, amiket kapunk, kézzelfoghatóak. Jóval nagyobb fejlesztési sebesség, kevesebb hibalehetőség, könnyebb tesztelhetőség. Kapunk olyan eszközöket is, melyek alkalmazásunk nyitottságát növelik. Szabványos protokollokat fogunk használni a kommunikációk során, melyeket szintén a háttérbeli motor fog nekünk nyújtani, a részletekkel nem kell törődni. Alkalmazásunkhoz más programozók, más programozási nyelven írt

programok is képesek lesznek csatlakozni. A világ részben összeszűkül, részben kitárul. Érdemes megismerni ezt a világot.

Az alábbi technikákról lesz még szó:

• Remoting

• Web Service

• Windows Communication Foundation

8. fejezet - .NET Remoting

A 7.2.5. alszakaszban leírt számológép szerver programunk az alacsony szintű kommunikációs módszerek segítségével készült el. Vizsgáljuk meg alaposabban, miről is szól ez a feladat és ez a megoldás!

A kliens oldalról az „OSSZEAD|12|14” szemszögből kommunikációs esetében nézve nem másról van szó, mint hogy a szerver oldalon meghívunk egy „függvényt” (két paraméterrel), amely egy újabb számot állít elő (számol ki), melyet visszaad a hívás helyére. Egy függvényhívást valósítunk meg. A függvény nevét és a paramétereket küldjük át, majd visszakapjuk a kiszámolt értéket.

A távoli (remote) eljáráshívás (procedure call) egy éppen ezzel foglalkozó módszer. Ugyanakkor a C#, .NET világában egyszerű függvényhívások nem léteznek, a függvények mindig valamelyik objektumosztályba vannak becsomagolva, s innentől kezdve a nevük nem „eljárás” vagy „függvény”, hanem metódus. Esetünkben most nem fogjuk folyamatosan hangsúlyozni ezt a különbséget.

Vagyis távoli metódushívást hajtunk végre, ahol a metódus kódja, a törzs az utasításokkal a szerveren foglal helyet, mi pedig a kliens oldalról aktiváljuk, hívjuk meg.

A hasonlóság adja magát. Ha függvényt hívunk, megadjuk a nevét, felsoroljuk és megadjuk a paraméterek értékeit. Van egy lényeges különbség: ha string alakba rakjuk a függvény nevét, a paraméterek értékét, akkor a fordítóprogram nem fogja ellenőrizni, hogy minden rendben van-e. Elírhatjuk a funkció nevét, túl kevés vagy túl sok paramétert küldhetünk át, nem megfelelő paramétertípust alkalmazhatunk, rosszul választhatjuk meg az elválasztó karaktert stb. Mindezek a szintaktikai hiba egyféle megjelenései, de sajnos nincs hozzá fordító támogatásunk. Ezek a hibák csak futás közben fognak jelentkezni.

Újabb problémák is vannak a stringalapú módszerrel. A szerver oldalon keletkező hibák, kivételek a kliens oldalra nem kivételek formájában érkeznek, hanem stringbe csomagolt hibaüzenet, hibakód alakjában, s hatásuk, kezelésük nem automatikus, mint ahogy azt az OOP-ben megszokhattuk. Sosem szerencsés egy megszokott automatizmust másra cserélni.

Az RPC (Remote Procedure Call) során a szerveren elkészítünk néhány függvényt. A függvények nevét, a paraméterlistát rögzítsük egy interfészben, majd készítsünk belőle DLL-t1! A DLL azért jó választás, mert a függvények törzsében lévő kódok nélkül ez egy nagyon kis méretű DLL lesz, könnyen és gyorsan le lehet tölteni a kliens oldalon csakúgy, mint a szerver oldalon.

A kliens oldalon a DLL-re azért lesz szükség, hogy a benne szereplő függvényhívások neveit, paramétereit, a visszatérési értékek helyes használatát a forráskódban a fordítóprogram le tudja ellenőrizni. A szerver oldalon azért van szükség az interfészre, mert ezeknek a függvényeknek kell megírni a kódját. Ezt a kapcsolatot a kliens és a szerver között a WCF-ben (Windows Communication Foundation) szerződésnek nevezzük.

namespace Szolgaltatas {

public interface ISzamologep {

Amikor a Visual Studio-val DLL-t szeretnénk írni, akkor projekt típusnak a „Class Library”-t kell választani.

8.1. ábra. A Class Library projekttípus

1A DLL régebbi elnevezés, a Win16, Win32 programozási modell részeként fontos szerepet játszott. A .NET Framework modellben az új neve assembly (szerelvény), de a generált fájl kiterjesztése a diszken továbbra is .dll lesz

A rövid kód begépelése után a class library projektet fordítsuk le! A lemezen kapunk egy (esetünkben) Szamologep.dll nevű fájlt. Ezen fájl az interfészt tartalmazza bináris, lefordított állapotban. Készítsük el először a szervert!