Útmutató az NIO2 aszinkron foglalatú csatornához

1. Áttekintés

Ebben a cikkben bemutatjuk, hogyan lehet egyszerű szervert és kliensét felépíteni a Java 7 NIO.2 csatorna API-k segítségével.

Megnézzük a AsynchronousServerSocketChannel és AsynchronousSocketChannel osztályok, amelyek a kiszolgáló és az ügyfél megvalósításában használt kulcsosztályok.

Ha Ön még nem ismeri a NIO.2 csatornás API-kat, akkor ezen a webhelyen van egy bevezető cikkünk. Ezt a linket követve olvashatja el.

A NIO.2 csatorna API-k használatához szükséges összes osztály csomagba van foglalva java.nio.csatornák csomag:

import java.nio.channels. *;

2. A szerver vele Jövő

Példája AsynchronousServerSocketChannel úgy jön létre, hogy meghívja a statikus nyílt API-t az osztályában:

AsynchronousServerSocketChannel server = AsynchronousServerSocketChannel.open ();

Egy újonnan létrehozott aszinkron kiszolgáló socket csatorna nyitva van, de még nincs kötve, ezért össze kell kötnünk egy helyi címmel, és opcionálisan ki kell választanunk egy portot:

server.bind (új InetSocketAddress ("127.0.0.1", 4555));

Ugyanolyan jól adhattuk volna át null-ban, hogy helyi címet használjon és egy tetszőleges porthoz kötődjön:

server.bind (null);

A kötést követően a elfogad Az API-t a kapcsolatok elfogadásának kezdeményezésére használják a csatorna foglalatához:

Future acceptFuture = server.accept ();

Az aszinkron csatornaműveletekhez hasonlóan a fenti hívás azonnal visszatér, és a végrehajtás folytatódik.

Ezután használhatjuk a kap API válasz lekérdezésére a Jövő tárgy:

AsynchronousSocketChannel dolgozó = jövő.get ();

Ez a hívás blokkolódik, ha szükséges, várja meg az ügyfél csatlakozási kérését. Opcionálisan megadhatunk egy időkorlátot is, ha nem akarunk örökké várni:

AsynchronousSocketChannel worker = acceptFuture.get (10, TimeUnit.SECONDS);

Miután a fenti hívásvisszatérés és a művelet sikeres volt, létrehozhatunk egy kört, amelyen belül meghallgatjuk a bejövő üzeneteket, és visszhangozzuk őket az ügyfélnek.

Hozzunk létre egy úgynevezett módszert runServer amelyen belül elvégezzük a várakozást és feldolgozzuk a beérkező üzeneteket:

public void runServer () {clientChannel = acceptResult.get (); if ((clientChannel! = null) && (clientChannel.isOpen ())) {while (true) {ByteBuffer puffer = ByteBuffer.allocate (32); Future readResult = clientChannel.read (puffer); // egyéb számítások végrehajtása readResult.get (); puffer.flip (); Future writeResult = clientChannel.write (puffer); // egyéb számítások végrehajtása writeResult.get (); puffer.clear (); } clientChannel.close (); serverChannel.close (); }}

A hurok belsejében annyit teszünk, hogy létrehozunk egy puffert, ahonnan a művelettől függően olvashatunk és írhatunk.

Ezután minden alkalommal, amikor olvasunk vagy írunk, folytathatjuk bármely más kód végrehajtását, és amikor készen állunk az eredmény feldolgozására, felhívjuk a kap() API a Jövő tárgy.

A szerver indításához hívjuk meg a konstruktort, majd a runServer módszer belül fő-:

public static void main (String [] args) {AsyncEchoServer server = új AsyncEchoServer (); server.runServer (); }

3. A szerver vele BefejezésKezelő

Ebben a szakaszban megtudjuk, hogyan lehet ugyanazt a szervert megvalósítani a BefejezésKezelő megközelítés helyett a Jövő megközelítés.

A konstruktor belsejében létrehozunk egy AsynchronousServerSocketChannel és kösse össze egy helyi címmel, ugyanúgy, mint korábban:

serverChannel = AsynchronousServerSocketChannel.open (); InetSocketAddress hostAddress = new InetSocketAddress ("localhost", 4999); serverChannel.bind (hostAddress);

Ezután még a konstruktor belsejében létrehozunk egy while ciklust, amelyen belül elfogadjuk az ügyféltől érkező esetleges bejövő kapcsolatokat. Ezt a ciklust szigorúan arra használják megakadályozza a kiszolgáló kilépését, mielőtt kapcsolatot létesít az ügyféllel.

Nak nek megakadályozza a hurok végtelen futását, hívjuk System.in.read () végén blokkolja a végrehajtást, amíg be nem jön egy beolvasott kapcsolat a szokásos bemeneti adatfolyamból:

while (true) {serverChannel.accept (null, new CompletionHandler () {@ A nyilvános érvénytelenítés befejezése (AsynchronousSocketChannel eredmény, objektum-melléklet) {if (serverChannel.isOpen ()) {serverChannel.accept (null, ez);} clientChannel = eredmény; if ((clientChannel! = null) && (clientChannel.isOpen ())) {ReadWriteHandler kezelő = new ReadWriteHandler (); ByteBuffer puffer = ByteBuffer.allocate (32); Térkép readInfo = új HashMap (); readInfo.put ( "action", "read"); readInfo.put ("puffer", puffer); clientChannel.read (puffer, readInfo, kezelő);}} @ A nyilvános érvénytelenítés felülbírálása sikertelen (dobható exc, objektum melléklet) {// folyamathiba }}); System.in.read (); }

A kapcsolat létrejötte után a elkészült visszahívási módszer a BefejezésKezelő az elfogadás művelet.

Visszatérési típusa a AsynchronousSocketChannel. Ha a kiszolgáló socket csatornája még nyitva van, akkor a elfogad Ismét API-t, hogy felkészülhessen egy újabb bejövő kapcsolatra, miközben ugyanazt a kezelőt használja újra.

Ezután hozzárendeljük a visszaküldött socket csatornát egy globális példányhoz. Ezután ellenőrizzük, hogy nem null, és nyitva van-e, mielőtt műveleteket hajtunk végre rajta.

Az a pont, amikor elkezdhetjük az olvasási és írási műveleteket, a elkészült visszahívási API-ja elfogad művelet kezelője. Ez a lépés felváltja a korábbi megközelítést, ahol a csatornát a kap API.

Figyelje meg a kapcsolat létrejötte után a kiszolgáló nem fog kilépni hacsak nem zárjuk be kifejezetten.

Vegye figyelembe azt is, hogy külön belső osztályt hoztunk létre az olvasási és írási műveletek kezelésére; ReadWriteHandler. Meglátjuk, hogy a melléklet tárgya ezen a ponton hogyan jön jól.

Először nézzük meg a ReadWriteHandler osztály:

osztályú ReadWriteHandler hajtja végre a CompletionHandlert {@ A nyilvános érvénytelenítés felülírása befejezve (Egész eredmény, Térkép-melléklet) {Map actionInfo = melléklet; Karakterlánc-művelet = (String) actionInfo.get ("action"); if ("read" .equals (action)) {ByteBuffer buffer = (ByteBuffer) actionInfo.get ("buffer"); puffer.flip (); actionInfo.put ("action", "write"); clientChannel.write (puffer, actionInfo, ez); puffer.clear (); } else if ("write" .equals (action)) {ByteBuffer puffer = ByteBuffer.allocate (32); actionInfo.put ("action", "read"); actionInfo.put ("puffer", puffer); clientChannel.read (puffer, actionInfo, ez); }} A @Orride public void sikertelen (Throwable exc, Map melléklet) {//}}

Csatolásunk általános típusa a ReadWriteHandler osztály egy térkép. Különösen két fontos paramétert kell átadnunk rajta - a művelet típusát (műveletet) és a puffert.

Ezután meglátjuk, hogyan használják ezeket a paramétereket.

Az első művelet, amelyet végrehajtunk, a olvas mivel ez egy visszhangkiszolgáló, amely csak az ügyfél üzeneteire reagál. Benne ReadWriteHandler’S elkészült visszahívási módszer, lekérjük a csatolt adatokat, és ennek megfelelően döntjük el, mit tegyünk.

Ha ez a olvas A befejezett művelet után lekérjük a puffert, megváltoztatjuk a melléklet műveleti paraméterét és végrehajtjuk a ír azonnal visszhangozza az üzenetet az ügyfélnek.

Ha ez a ír a most befejezett műveletet hívjuk olvas Ismét API-t, hogy felkészítse a szervert egy újabb bejövő üzenet fogadására.

4. Az Ügyfél

A szerver beállítása után mostantól beállíthatjuk a klienst a nyisd ki API a AsyncronousSocketChannel osztály. Ez a hívás létrehoz egy új példányt a kliens socket csatornáról, amelyet ezután használunk a szerverhez való kapcsolódáshoz:

AsynchronousSocketChannel kliens = AsynchronousSocketChannel.open (); InetSocketAddress hostAddress = new InetSocketAddress ("localhost", 4999) Future future = client.connect (hostAddress);

A csatlakozzon a művelet semmit sem hoz a sikerhez. Azonban továbbra is használhatjuk a Jövő objektum az aszinkron működés állapotának figyelemmel kísérésére.

Hívjuk a kap API várja a kapcsolatot:

future.get ()

Ezt a lépést követően megkezdhetjük az üzenetek küldését a szerverre, és visszhangok fogadását. A üzenet küldése módszer így néz ki:

public String sendMessage (String üzenet) {byte [] byteMsg = új String (üzenet) .getBytes (); ByteBuffer buffer = ByteBuffer.wrap (byteMsg); Future writeResult = client.write (puffer); // végezzen néhány számítást writeResult.get (); puffer.flip (); Future readResult = client.read (puffer); // végezzen némi számítást a readResult.get (); String visszhang = new String (puffer.array ()). Trim (); puffer.clear (); visszatérő visszhang; }

5. A teszt

Tesztet használhatunk annak igazolására, hogy szerver- és kliensalkalmazásaink az elvárásoknak megfelelően működnek

@Test public void givenServerClient_whenServerEchosMessage_thenCorrect () {String resp1 = client.sendMessage ("hello"); Karakterlánc resp2 = client.sendMessage ("világ"); assertEquals ("hello", resp1); assertEquals ("világ", ill2); }

6. Következtetés

Ebben a cikkben feltártuk a Java NIO.2 aszinkron socket csatorna API-kat. Ezekkel az új API-kkal át tudtuk lépni a szerver és az ügyfél felépítését.

A cikk teljes forráskódját a Github projektben érheti el.