Sprievodca po asynchrónnom zásuvkovom kanáli NIO2

1. Prehľad

V tomto článku si ukážeme, ako vytvoriť jednoduchý server a jeho klienta pomocou kanálových rozhraní API Java 7 NIO.2.

Pozrime sa na AsynchronousServerSocketChannel a AsynchronousSocketChannel triedy, ktoré sú kľúčovými triedami používanými pri implementácii servera a klienta.

Ak ste v rozhraní API kanála NIO.2 nováčikom, máme na tejto stránke úvodný článok. Môžete si ho prečítať po kliknutí na tento odkaz.

Všetky triedy, ktoré sú potrebné na používanie API kanálov NIO.2, sú zoskupené v java.nio.kanály balenie:

import java.nio.channels. *;

2. Server s Budúcnosť

Prípad z AsynchronousServerSocketChannel sa vytvára volaním statického otvoreného API na svojej triede:

AsynchronousServerSocketChannel server = AsynchronousServerSocketChannel.open ();

Novo vytvorený asynchrónny kanál soketu servera je otvorený, ale ešte nie je viazaný, takže ho musíme naviazať na miestnu adresu a voliteľne zvoliť port:

server.bind (nová InetSocketAddress ("127.0.0.1", 4555));

Rovnako sme mohli prejsť na hodnotu null, aby používala miestnu adresu a viazala sa na ľubovoľný port:

server.bind (null);

Po zviazaní sa súhlasiť API sa používa na inicializáciu prijímania pripojení k zásuvke kanála:

Budúcnosť acceptFuture = server.accept ();

Rovnako ako v prípade asynchrónnych operácií s kanálom, vyššie uvedené volanie sa vráti okamžite a vykonávanie pokračuje.

Ďalej môžeme použiť dostať API na dopytovanie odpovede z Budúcnosť objekt:

AsynchronousSocketChannel worker = future.get ();

Toto volanie sa v prípade potreby zablokuje, aby čakalo na žiadosť klienta o pripojenie. Voliteľne môžeme určiť časový limit, ak nechceme čakať večne:

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

Po vyššie uvedených návratoch hovoru a operácii boli úspešné, môžeme vytvoriť slučku, v rámci ktorej počúvame prichádzajúce správy a ozývame ich späť na klienta.

Vytvorme metódu s názvom runServer v rámci ktorého urobíme čakanie a spracujeme všetky prichádzajúce správy:

public void runServer () {clientChannel = acceptResult.get (); if ((clientChannel! = null) && (clientChannel.isOpen ())) {while (true) {ByteBuffer buffer = ByteBuffer.allocate (32); Budúci readResult = clientChannel.read (buffer); // vykonať ďalšie výpočty readResult.get (); buffer.flip (); Budúci writeResult = clientChannel.write (buffer); // vykonať ďalšie výpočty writeResult.get (); buffer.clear (); } clientChannel.close (); serverChannel.close (); }}

Vo vnútri slučky všetko, čo robíme, je vytvorenie medzipamäte na čítanie a zápis do, v závislosti od operácie.

Potom, kedykoľvek vykonáme čítanie alebo zápis, môžeme pokračovať v vykonávaní ľubovoľného iného kódu a keď sme pripravení spracovať výsledok, zavoláme dostať () API na Budúcnosť objekt.

Server spustíme tak, že zavoláme jeho konštruktor a potom znak runServer metóda vo vnútri hlavný:

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

3. Server s CompletionHandler

V tejto časti uvidíme, ako implementovať ten istý server pomocou CompletionHandler skôr prístup ako a Budúcnosť prístup.

Vo vnútri konštruktora vytvoríme AsynchronousServerSocketChannel a naviazať ho na miestnu adresu rovnakým spôsobom ako predtým:

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

Ďalej, stále vo vnútri konštruktora, vytvoríme while slučku, v rámci ktorej prijímame akékoľvek prichádzajúce spojenie od klienta. Táto slučka while sa používa striktne na zabrániť nadviazaniu servera pred nadviazaním spojenia s klientom.

To zabrániť nekonečnému chodu slučky, voláme System.in.read () na konci blokuje vykonávanie, kým sa prichádzajúce spojenie neprečíta zo štandardného vstupného toku:

while (true) {serverChannel.accept (null, new CompletionHandler () {@Override public void completed (výsledok AsynchronousSocketChannel, príloha objektu) {if (serverChannel.isOpen ()) {serverChannel.accept (null, this);} clientChannel = result; if ((clientChannel! = null) && (clientChannel.isOpen ())) {ReadWriteHandler handler = new ReadWriteHandler (); ByteBuffer buffer = ByteBuffer.allocate (32); Map readInfo = new HashMap (); readInfo.put ( "action", "read"); readInfo.put ("buffer", buffer); clientChannel.read (buffer, readInfo, handler);}} @Override public void failed (Throwable exc, Object attachment) {// chyba procesu }}); System.in.read (); }

Po nadviazaní spojenia sa zobrazí ikona dokončené metóda spätného volania v CompletionHandler sa volá operácia prijatia.

Jeho návratový typ je inštanciou AsynchronousSocketChannel. Ak je kanál zásuvky servera stále otvorený, hovoríme súhlasiť Rozhranie API znova, aby ste sa pripravili na ďalšie prichádzajúce pripojenie pri opätovnom použití rovnakého obslužného programu.

Ďalej priradíme vrátený soketový kanál globálnej inštancii. Pred vykonaním operácií s ním potom skontrolujeme, či nie je prázdny a či je otvorený.

Bod, v ktorom môžeme začať s operáciami čítania a zápisu, je vo vnútri dokončené API spätného volania súhlasiť obsluha operácie. Tento krok nahradzuje predchádzajúci prístup, keď sme kanál zisťovali pomocou dostať API.

Všimni si server sa po nadviazaní spojenia už nezastaví pokiaľ to výslovne nezatvoríme.

Všimnite si tiež, že sme vytvorili samostatnú vnútornú triedu pre manipuláciu s operáciami čítania a zápisu; ReadWriteHandler. Uvidíme, ako sa nám v tejto chvíli bude hodiť pripojovací objekt.

Najprv sa pozrime na ReadWriteHandler trieda:

trieda ReadWriteHandler implementuje CompletionHandler {@Override public void completed (celočíselný výsledok, mapová príloha) {map actionInfo = príloha; String action = (String) actionInfo.get ("action"); if ("read" .equals (action)) {ByteBuffer buffer = (ByteBuffer) actionInfo.get ("buffer"); buffer.flip (); actionInfo.put ("action", "write"); clientChannel.write (buffer, actionInfo, this); buffer.clear (); } else if ("write" .equals (action)) {ByteBuffer buffer = ByteBuffer.allocate (32); actionInfo.put ("action", "read"); actionInfo.put ("buffer", buffer); clientChannel.read (buffer, actionInfo, this); }} @Override public void failed (Hádzateľný exc, príloha mapy) {//}}

Všeobecný typ našej prílohy v ReadWriteHandler trieda je mapa. Cez ňu musíme špeciálne prejsť dvoma dôležitými parametrami - typ operácie (akcia) a vyrovnávacia pamäť.

Ďalej uvidíme, ako sa tieto parametre používajú.

Prvá operácia, ktorú vykonáme, je a čítať pretože toto je echo server, ktorý reaguje iba na správy klientov. Vnútri ReadWriteHandler‘S dokončené metóda spätného volania, načítame pripojené údaje a podľa toho sa rozhodneme, čo urobíme.

Ak je to čítať operácia, ktorá bola dokončená, načítame vyrovnávaciu pamäť, zmeníme akčný parameter prílohy a vykonáme a napíš operáciu, aby sa ozvala správa klientovi.

Ak je to napíš operácia, ktorá sa práve dokončila, nazývame čítať API znova, aby sa server pripravil na príjem ďalšej prichádzajúcej správy.

4. Klient

Po nastavení servera môžeme teraz nastaviť klienta zavolaním na otvorené API na AsyncronousSocketChannel trieda. Toto volanie vytvorí novú inštanciu kanála klientskej zásuvky, ktorú potom použijeme na vytvorenie spojenia so serverom:

AsynchronousSocketChannel client = AsynchronousSocketChannel.open (); InetSocketAddress hostAddress = nová InetSocketAddress ("localhost", 4999) Budúca budúcnosť = client.connect (hostAddress);

The spojiť operácia nevráti nič na úspech. Stále však môžeme použiť Budúcnosť objekt monitorovať stav asynchrónnej operácie.

Zavolajme dostať API čakajúce na pripojenie:

future.get ()

Po tomto kroku môžeme začať odosielať správy na server a prijímať ozveny pre ne. The poslať správu metóda vyzerá takto:

public String sendMessage (String message) {byte [] byteMsg = new String (message) .getBytes (); ByteBuffer buffer = ByteBuffer.wrap (byteMsg); Budúci writeResult = client.write (buffer); // urobte nejaký výpočet writeResult.get (); buffer.flip (); Budúci readResult = client.read (buffer); // urobte nejaký výpočet readResult.get (); Reťazec echo = nový Reťazec (buffer.array ()). Trim (); buffer.clear (); spätná ozvena; }

5. Test

Aby sme potvrdili, že naše serverové a klientske aplikácie fungujú podľa očakávaní, môžeme použiť test:

@Test public void givenServerClient_whenServerEchosMessage_thenCorrect () {String resp1 = client.sendMessage ("ahoj"); Reťazec resp2 = client.sendMessage ("svet"); assertEquals ("ahoj", resp1); assertEquals ("svet", resp2); }

6. Záver

V tomto článku sme preskúmali rozhrania API asynchrónneho socketového kanálu Java NIO.2. Boli sme schopní prejsť procesom budovania servera a klienta pomocou týchto nových rozhraní API.

Celý zdrojový kód tohto článku nájdete v dokumente Github.


$config[zx-auto] not found$config[zx-overlay] not found