Vodič za NIO2 asinkroni utičnički kanal

1. Pregled

U ovom ćemo članku pokazati kako izraditi jednostavni poslužitelj i njegovog klijenta pomoću Java 7 NIO.2 API-ja za kanale.

Pogledat ćemo AsynchronousServerSocketChannel i AsynchronousSocketChannel klase koje su ključne klase korištene u implementaciji poslužitelja odnosno klijenta.

Ako ste novi u NIO.2 API-jevima za kanale, na ovom mjestu imamo uvodni članak. Možete ga pročitati slijedeći ovu poveznicu.

Uključene su sve klase potrebne za upotrebu API-ja NIO.2 kanala java.nio.kanali paket:

import java.nio.channels. *;

2. Poslužitelj sa Budućnost

Primjer AsynchronousServerSocketChannel kreira se pozivanjem statičkog otvorenog API-ja u svojoj klasi:

AsynchronousServerSocketChannel server = AsynchronousServerSocketChannel.open ();

Novostvoreni kanal asinkronog poslužiteljskog soketa je otvoren, ali još nije vezan, pa ga moramo povezati s lokalnom adresom i po želji odabrati priključak:

server.bind (nova InetSocketAddress ("127.0.0.1", 4555));

Mogli smo jednako tako proslijediti nulu tako da koristi lokalnu adresu i veže se za proizvoljan port:

server.bind (null);

Jednom vezan, prihvatiti API se koristi za iniciranje prihvaćanja veza na utičnicu kanala:

Budućnost acceptFuture = server.accept ();

Kao što je to slučaj s operacijama asinkronog kanala, gornji se poziv odmah vraća i izvršavanje se nastavlja.

Dalje, možemo koristiti dobiti API na upit za odgovor od Budućnost objekt:

AsynchronousSocketChannel worker = future.get ();

Ako je potrebno, ovaj će poziv blokirati čekanje zahtjeva klijenta za povezivanjem. Po želji možemo odrediti vremensko ograničenje ako ne želimo čekati vječno:

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

Nakon što se gornji poziv vrati i operacija bude uspješna, možemo stvoriti petlju unutar koje osluškujemo dolazne poruke i vraćamo ih natrag klijentu.

Stvorimo metodu tzv runServer unutar kojeg ćemo čekati i obraditi sve dolazne poruke:

javna void runServer () {clientChannel = acceptResult.get (); if ((clientChannel! = null) && (clientChannel.isOpen ())) {while (true) {ByteBuffer buffer = ByteBuffer.allocate (32); Budući readResult = clientChannel.read (međuspremnik); // izvodimo druge izračune readResult.get (); buffer.flip (); Budući writeResult = clientChannel.write (međuspremnik); // izvodimo druge izračune writeResult.get (); buffer.clear (); } clientChannel.close (); serverChannel.close (); }}

Unutar petlje sve što radimo je stvoriti međuspremnik za čitanje i zapisivanje, ovisno o operaciji.

Zatim, svaki put kad čitamo ili pišemo, možemo nastaviti izvršavati bilo koji drugi kôd i kad smo spremni obraditi rezultat, pozivamo dobiti() API na Budućnost objekt.

Da bismo pokrenuli poslužitelj, nazivamo njegov konstruktor, a zatim runServer metoda iznutra glavni:

javna statička void glavna (String [] args) {AsyncEchoServer server = novi AsyncEchoServer (); server.runServer (); }

3. Poslužitelj sa Rukovatelj završetka

U ovom ćemo odjeljku vidjeti kako implementirati isti poslužitelj pomoću Rukovatelj završetka pristup nego a Budućnost pristup.

Unutar konstruktora kreiramo AsynchronousServerSocketChannel i vežite ga na lokalnu adresu na isti način kao i prije:

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

Dalje, još uvijek unutar konstruktora, kreiramo while petlju unutar koje prihvaćamo svaku dolaznu vezu od klijenta. Ova petlja while koristi se isključivo za spriječiti izlaz poslužitelja prije uspostavljanja veze s klijentom.

Do spriječiti beskrajno pokretanje petlje, mi zovemo System.in.read () na svom kraju da blokira izvršenje sve dok se dotična veza ne pročita iz standardnog ulaznog toka:

while (true) {serverChannel.accept (null, new CompletionHandler () {@Override javna praznina je dovršena (AsynchronousSocketChannel result, Object attachment) {if (serverChannel.isOpen ()) {serverChannel.accept (null, this);} clientChannel = rezultat; 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) {// greška procesa }}); System.in.read (); }

Kada se uspostavi veza, dovršen metoda povratnog poziva u Rukovatelj završetka operacije prihvaćanja naziva se.

Njegov je povratni primjer instanca AsynchronousSocketChannel. Ako je kanal utičnice poslužitelja i dalje otvoren, pozivamo prihvatiti API ponovo za pripremu za još jednu dolaznu vezu tijekom ponovne upotrebe istog rukovatelja.

Dalje, vraćeni kanal soketa dodjeljujemo globalnoj instanci. Zatim provjeravamo nije li null i je li otvoren prije izvođenja operacija na njemu.

Točka u kojoj možemo započeti operacije čitanja i pisanja nalazi se unutar dovršen API za povratni poziv prihvatiti voditelj operacije. Ovaj korak zamjenjuje prethodni pristup gdje smo kanal anketirali s dobiti API.

Primijeti da poslužitelj više neće izlaziti nakon uspostavljanja veze osim ako ga izričito ne zatvorimo.

Također primijetite da smo stvorili zasebnu unutarnju klasu za rukovanje operacijama čitanja i pisanja; ReadWriteHandler. Vidjet ćemo kako će vam objekt vezanja u ovom trenutku dobro doći.

Prvo, pogledajmo ReadWriteHandler razred:

klasa ReadWriteHandler implementira CompletionHandler {@Preuzmi javnu prazninu dovršeno (cijeli broj, prilog karte) {Map actionInfo = prilog; String action = (String) actionInfo.get ("action"); if ("read" .equals (action)) {ByteBuffer buffer = (ByteBuffer) actionInfo.get ("buffer"); buffer.flip (); actionInfo.put ("action", "write"); clientChannel.write (međuspremnik, actionInfo, ovo); buffer.clear (); } else if ("write" .equals (action)) {ByteBuffer buffer = ByteBuffer.allocate (32); actionInfo.put ("akcija", "čitanje"); actionInfo.put ("međuspremnik", međuspremnik); clientChannel.read (međuspremnik, actionInfo, ovo); }} @Override javna praznina nije uspjela (bez mogućnosti bacanja, privitak karte) {//}}

Generički tip naše privrženosti u ReadWriteHandler razred je karta. Kroz nju posebno moramo propustiti dva važna parametra - vrstu operacije (radnje) i međuspremnik.

Dalje ćemo vidjeti kako se ti parametri koriste.

Prva operacija koju izvodimo je čitati budući da je ovo echo poslužitelj koji reagira samo na poruke klijenta. Unutar ReadWriteHandler‘S dovršen metoda povratnog poziva, dohvaćamo priložene podatke i u skladu s tim odlučujemo što ćemo učiniti.

Ako je a čitati operacija koja je dovršena, dohvaćamo međuspremnik, mijenjamo parametar akcije privitka i izvodimo a pisati operacija za odjek poruke klijentu.

Ako je a pisati operacija koja je upravo završila, nazivamo čitati API ponovo za pripremu poslužitelja za primanje nove dolazne poruke.

4. Klijent

Nakon postavljanja poslužitelja, sada možemo postaviti klijenta pozivom na otvorena API na AsyncronousSocketChannel razred. Ovaj poziv stvara novu instancu klijentskog kanala utičnice koju zatim koristimo za uspostavu veze s poslužiteljem:

AsynchronousSocketChannel client = AsynchronousSocketChannel.open (); InetSocketAddress hostAddress = novi InetSocketAddress ("localhost", 4999) Buduća budućnost = client.connect (hostAddress);

The Spojiti operacija ništa ne vraća na uspjeh. Međutim, još uvijek možemo koristiti Budućnost objekt za nadgledanje stanja asinkrone operacije.

Nazovimo dobiti API za čekanje veze:

future.get ()

Nakon ovog koraka možemo početi slati poruke na poslužitelj i primati odjeke za iste. The Pošalji poruku metoda izgleda ovako:

javni String sendMessage (String poruka) {byte [] byteMsg = novi String (poruka) .getBytes (); ByteBuffer međuspremnik = ByteBuffer.wrap (byteMsg); Budući writeResult = client.write (međuspremnik); // napravimo neko računanje writeResult.get (); buffer.flip (); Budući readResult = client.read (međuspremnik); // napravimo neko računanje readResult.get (); String echo = novi String (buffer.array ()). Trim (); buffer.clear (); povratni odjek; }

5. Test

Da bismo potvrdili da naše poslužiteljske i klijentske aplikacije rade prema očekivanjima, možemo koristiti test:

@Test javna praznina givenServerClient_whenServerEchosMessage_thenCorrect () {String resp1 = client.sendMessage ("hello"); Niz resp2 = client.sendMessage ("svijet"); assertEquals ("zdravo", resp1); assertEquals ("svijet", odnosno 2); }

6. Zaključak

U ovom smo članku istražili API-je asinkronog kanala utičnice Java NIO.2. Uspjeli smo proći kroz proces izgradnje poslužitelja i klijenta s tim novim API-ima.

Punom izvornom kodu za ovaj članak možete pristupiti u projektu Github.


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