• Nem Talált Eredményt

Összeköttetés-alapú kommunikáció megvalósítása

In document Szoftverfejlesztés II. (Pldal 97-101)

7. A hálózatkezelés osztályai

7.2.2 Összeköttetés-alapú kommunikáció megvalósítása

Az összeköttetés-alapú kommunikáció szerver oldalát Javában a ServerSocket osztály valósítja meg, ez egy TCP kommunikációs port abszt-rakciója. Két konstruktorát szokás használni:

 ServerSocket(int port): egy egész számként meg kell adni a szerver számára lefoglalandó TCP-port számát

 ServerSocket(int port, int sm): első paraméter itt is a portszám, a második a szerver várakozási sorának mérete, vagyis hogy a szerverrel való kommunikációra mennyi kliens várakozhat. Az alapér-telmezés szerinti érték 50.

A konstruktor egy IOException kivételt dobhat, amelynek lehetséges oka, hogy a lefoglalandó TCP-port már használatban van egy másik alkalmazás által, esetleg beteltek a rendszererőforrások és nincs lehetőség újabb TCP-port foglalására, az is előfordulhat, hogy némely operációs rendszer nem támogatja a hálózati protokollok elérését. Amennyiben a konstruktor sikerrel végrehajtó-dott, a kliensek már csatlakozhatnak a szerverhez.

A szerverhez csatlakozni akaró kliensek érkezési sorrendben egy várakozási sorba kerülnek, a szerver ebből a sorból az első klienssel hozhat létre kapcsola-tot a ServerSocket objektum accept metódusával. Amennyiben a várako-zási sor üres, akkor a metódus hívására a szerver addig vár, amíg egy kliens csatlakozni nem akar. Az accept metódus egy Socket objektumot ad vissza, ez valósítja meg a szerver-kliens közötti adatátviteli csatornának az egyik végét.

A socket a Javában egy végpontját jelenti egy kétirányú kommunikációs hálózati kapcsolatnak. A sockethez mindig tartozik egy IP-cím és egy portszám. A portszám alapján lehetőség van az adatcsomagok megfelelő alkalmazáshoz való juttatására. A Socket objektum rendelkezik egy getInputStream és egy getOutputStream metódussal, amelyekkel hozzáférhetünk a kommunikáci-ós csatorna feletti I/O-műveletekhez, ezekkel valkommunikáci-ósítható meg az adatátvitel a szerver és a kliens között. A szerveralkalmazás működésének befejezésekor meg kell hívni a ServerSocket objektum close metódusát, amely felsza-badítja a lefoglalt hálózati erőforrásokat (általában az operációs rendszer az alkalmazás befejezése után a close nélkül is felszabadítja az alkalmazás által lekötött hálózati erőforrásokat, de célszerű ezt nekünk megtenni). Egy szerver esetén különösen fontos, hogy az erőforrásokkal a lehető legjobban gazdálkod-junk!

Az kliens oldalon a szükséges szolgáltatásokat szintén a korábban említett Socket osztály valósítja meg. A leggyakrabban alkalmazott konstruktorában

meg kell adni a kommunikációs partner címét és portját. A cím lehet számító-gépnév és InetAddress objektummal megadott cím is. Az InetAddress osztály alkalmas a hálózatba kötött számítógépek IP-címének azonosítására.

Egy InetAddress objektum létrehozásához használhatjuk a következő stati-kus metódusokat:

 InetAddress getLocalHost(): a saját számítógépünk címét reprezentálja

 InetAddress getByName(String name): a paraméterben megadott számítógép címét reprezentálja, a name lehet pl. IP-cím is

 InetAddress[] getAllByName(String name): a paramé-terben megadott számítógép összes címét visszaadja egy tömbben A Socket osztály néhány hasznos metódusa a korábban említett getInputStream és getOutputStream mellett:

 InetAddress getLocalAddress(): a kommunikációs végpont-ként szolgáló számítógép (vagyis a szerver) címét adja vissza

 InetAddress getInetAddress(): a kommunikációs partner (kliens) címét adja vissza

 int getLocalPort(): a kommunikációs végpont portját adja visz-sza

 int getPort(): a kommunikációs partner portját adja vissza

Amennyiben a kommunikációs kapcsolatra a továbbiakban nincs szükség, itt is meg kell hívni a close metódust a rendszererőforrások felszabadításá-hoz.

A következő feladat egy olyan összeköttetés-alapú szerver-kliens alkalma-zás megvalósítása, ahol a szerver meghatározza a kliens által küldött szám prím-tényezős felbontását, és természetesen ezt a kliens felé továbbítja is. A szervert párhuzamos szerkezetűként valósítjuk meg. Nézzük a szerverért felelős kódot, és utána rátérünk a magyarázatra:

1 public class TCPServerThreaded {

2 public static void main(String[] args) {

10 }

11 } catch (IOException ex) {}

12 } 13 }

14 class ServerThread extends Thread { 15 private Socket s;

42 public static String primeResolution(int value) {

49 }

A szerverünk a 9980-as porton fog futni. Elindítjuk a szervert (6. sor), majd várakozunk egy kliens kapcsolódási kérelmére az ss.accept()-tel. A szerve-rünk nem fog leállni a while(true) miatt. Be lehetne vezetni egy logikai változót a true helyett, amelynek valamilyen feltételtől függően változna az értéke, és a szerverünk leállna. Az accept által visszaadott sockettel elindí-tunk egy ServerThread szálat.

A ServerThread osztályt a Thread-ből származtatjuk (a párhuzamos programozásról késbb lesz szó), ez teszi lehetővé, hogy osztályunk egy példá-nyát programszálként futtassuk. A konstruktor egy Socket objektumot vár paraméterként. A start() (9. sor) hatására a ServerThread példányunk run() metódusa fog elindulni. Az input és output változókban eltároljuk a sockethez tartozó bemeneti és kimeneti I/O-csatornákat, majd az in-put.readLine() segítségével beolvasunk egy sort a socketről, amelyet a kliens küldött. A parseInt metódussal int-té alakítjuk (NumberFormatException kivétel váltódhat ki), majd a kimenetre (out-put) írjuk az átalakított érték prímtényezős felbontását, amelyet majd a kliens olvas be. A szerverünk ezzel elvégezte feladatát, a finally blokkban lezárjuk az I/O-csatornákat és a socketet. A ServerThread-nek van még egy statikus primeResolution metódusa, amely egy int érték prímtényezős felbontá-sát adja vissza sztringként.

Nézzük meg a kliensoldali alkalmazást:

1 public class TCPClient {

2 public static void main(String[] args) {

12 output=new egy helyi hálózatba tartozó gép IP-címe lesz (ezen fut a szerver). A try blokk-ban létrehozunk egy InetAddress példányt a socket példányosításához, lekérjük a hozzá tartozó I/O csatornákat, majd a szerver felé elküldjük a 258-as értéket (ennek kapjuk majd vissza a prímtényezős felbontását), majd ezután az input-ról beolvasunk egy sort, amely a szerver felől érkezik. A flush() azért kell, hogy a kimenetre írandó érték ne maradjon a pufferban. Végül a finally blokkban itt is lezárjuk az I/O-csatornákat és a socketet.

Hasznos tudni, hogy a szerver-kliens kommunikáció egyes lépéseinél meg lehet határozni időkorlátot. Ilyen lépés pl. a ServerSocket accept metó-dusa, vagy a Socket osztály getInputStream és getOutputStream metódusaival létrehozott adatcsatornák read metódusa, amely egy beolvasha-tó adat érkezéséig vár. Az időkorlát megadásával egy maximális várakozási időt állíthatunk be ezredmásodpercben, ezt a setSoTimeout(int i) metódus-sal tehetjük meg.

7.2.3 Összeköttetés-mentes kommunikáció

In document Szoftverfejlesztés II. (Pldal 97-101)