En guide til NIO2 Asynchronous Socket Channel

1. Oversikt

I denne artikkelen vil vi demonstrere hvordan du bygger en enkel server og klienten ved hjelp av Java 7 NIO.2-kanal-API-ene.

Vi ser på AsynchronousServerSocketChannel og AsynchronousSocketChannel klasser som er nøkkelklassene som brukes til å implementere henholdsvis serveren og klienten.

Hvis du ikke har brukt API-er for NIO.2-kanaler, har vi en innledende artikkel på dette nettstedet. Du kan lese den ved å følge denne lenken.

Alle klasser som er nødvendige for å bruke NIO.2-kanal-API-er, er samlet i java.nio.channels pakke:

importere java.nio.channels. *;

2. Serveren med Framtid

En forekomst av AsynchronousServerSocketChannel er opprettet ved å kalle den statiske åpne APIen på sin klasse:

AsynchronousServerSocketChannel server = AsynchronousServerSocketChannel.open ();

En nylig opprettet asynkron serveruttakskanal er åpen, men ennå ikke bundet, så vi må binde den til en lokal adresse og eventuelt velge en port:

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

Vi kunne like godt ha gått i null slik at den bruker en lokal adresse og binder seg til en vilkårlig port:

server.bind (null);

Når bundet, den aksepterer API brukes til å initiere aksept av tilkoblinger til kanalens kontakt:

Future acceptFuture = server.accept ();

Som det er med asynkrone kanaloperasjoner, kommer ovennevnte samtale tilbake med en gang og utførelse fortsetter.

Deretter kan vi bruke API for å søke etter svar fra Framtid gjenstand:

AsynchronousSocketChannel-arbeider = future.get ();

Denne samtalen vil blokkere om nødvendig for å vente på en tilkoblingsforespørsel fra en klient. Eventuelt kan vi spesifisere en tidsavbrudd hvis vi ikke vil vente for alltid:

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

Etter at samtalen er kommet tilbake og operasjonen var vellykket, kan vi lage en løkke der vi lytter etter innkommende meldinger og ekko dem tilbake til klienten.

La oss lage en metode som heter runServer der vi vil vente og behandle innkommende meldinger:

offentlig ugyldig runServer () {clientChannel = acceptResult.get (); if ((clientChannel! = null) && (clientChannel.isOpen ())) {while (true) {ByteBuffer buffer = ByteBuffer.allocate (32); Fremtidig readResult = clientChannel.read (buffer); // utføre andre beregninger readResult.get (); buffer.flip (); Fremtidig writeResult = clientChannel.write (buffer); // utføre andre beregninger writeResult.get (); buffer.clear (); } clientChannel.close (); serverChannel.close (); }}

Inne i løkken er alt vi gjør å lage en buffer å lese fra og skrive til, avhengig av operasjonen.

Hver gang vi leser eller skriver, kan vi fortsette å utføre en hvilken som helst annen kode, og når vi er klare til å behandle resultatet, kaller vi få() API på Framtid gjenstand.

For å starte serveren, kaller vi konstruktøren og deretter runServer metode inni hoved-:

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

3. Serveren med CompletionHandler

I denne delen vil vi se hvordan du implementerer den samme serveren ved hjelp av CompletionHandler tilnærming heller enn en Framtid nærme seg.

Inne i konstruktøren lager vi en AsynchronousServerSocketChannel og bind den til en lokal adresse på samme måte som vi gjorde før:

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

Deretter, fortsatt inne i konstruktøren, oppretter vi en stundsløyfe der vi godtar enhver innkommende forbindelse fra en klient. Dette mens loop brukes strengt til forhindre at serveren avslutter før du oppretter en forbindelse med en klient.

Til forhindre at løkken løper uendelig, vi ringer System.in.read () på slutten for å blokkere kjøringen til en innkommende forbindelse leses fra standard inngangsstrøm:

mens (true) {serverChannel.accept (null, new CompletionHandler () {@Override public void complete (AsynchronousSocketChannel result, Object attachment) {if (serverChannel.isOpen ()) {serverChannel.accept (null, this);} clientChannel = resultat; hvis ((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) {// process error }}); System.in.read (); }

Når en forbindelse opprettes, vil fullført tilbakeringingsmetode i CompletionHandler av godtaoperasjonen kalles.

Returtypen er en forekomst av AsynchronousSocketChannel. Hvis serveruttakskanalen fortsatt er åpen, kaller vi aksepterer API igjen for å gjøre deg klar for en ny innkommende forbindelse mens du bruker den samme håndtereren på nytt.

Deretter tilordner vi den returnerte stikkontaktkanalen til en global forekomst. Vi sjekker da at det ikke er null, og at det er åpent før vi utfører operasjoner på det.

Punktet der vi kan begynne å lese og skrive operasjoner er inne i fullført tilbakeringings-API for aksepterer operasjonens fører. Dette trinnet erstatter den forrige tilnærmingen der vi spurte kanalen med API.

Legg merke til det serveren vil ikke lenger avslutte etter at en forbindelse er opprettet med mindre vi eksplisitt lukker den.

Legg også merke til at vi opprettet en egen indre klasse for håndtering av lese- og skriveoperasjoner; ReadWriteHandler. Vi vil se hvordan vedleggsobjektet kommer til nytte på dette punktet.

La oss først se på ReadWriteHandler klasse:

klasse ReadWriteHandler implementerer CompletionHandler {@Override public void complete (Integer result, Map attachment) {Map actionInfo = vedlegg; String action = (String) actionInfo.get ("action"); if ("read" .equals (action)) {ByteBuffer buffer = (ByteBuffer) actionInfo.get ("buffer"); buffer.flip (); actionInfo.put ("handling", "skriv"); clientChannel.write (buffer, actionInfo, dette); buffer.clear (); } annet hvis ("skriv" .equals (handling)) {ByteBuffer buffer = ByteBuffer.allocate (32); actionInfo.put ("action", "read"); actionInfo.put ("buffer", buffer); clientChannel.read (buffer, actionInfo, dette); }} @ Override public void failed (Throwable exc, Map attachment) {//}}

Den generiske typen vårt vedlegg i ReadWriteHandler klasse er et kart. Vi må spesifikt passere to viktige parametere gjennom den - typen operasjon (handling) og bufferen.

Deretter vil vi se hvordan disse parametrene brukes.

Den første operasjonen vi utfører er en lese siden dette er en ekkoserver som bare reagerer på klientmeldinger. Inne i ReadWriteHandler‘S fullført tilbakeringingsmetode, henter vi vedlagte data og bestemmer hva vi skal gjøre i samsvar med dette.

Hvis det er en lese operasjonen som er fullført, henter vi bufferen, endrer handlingsparameteren for vedlegget og utfører en skrive operasjon med en gang for å ekko meldingen til klienten.

Hvis det er en skrive operasjonen som nettopp er fullført, kaller vi lese API igjen for å forberede serveren til å motta en ny innkommende melding.

4. Kunden

Etter å ha satt opp serveren, kan vi nå sette opp klienten ved å ringe åpen API på AsyncronousSocketChannel klasse. Denne samtalen oppretter en ny forekomst av klientuttakskanalen som vi deretter bruker til å opprette en forbindelse til serveren:

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

De koble operasjonen gir ikke noe tilbake på suksess. Imidlertid kan vi fortsatt bruke Framtid motstand for å overvåke tilstanden til den asynkrone operasjonen.

La oss kalle API for å avvente tilkobling:

future.get ()

Etter dette trinnet kan vi begynne å sende meldinger til serveren og motta ekko for det samme. De sende melding metoden ser slik ut:

offentlig streng sendMessage (strengmelding) {byte [] byteMsg = ny streng (melding) .getBytes (); ByteBuffer buffer = ByteBuffer.wrap (byteMsg); Fremtidig writeResult = client.write (buffer); // gjøre noe beregning writeResult.get (); buffer.flip (); Fremtidig readResult = client.read (buffer); // gjøre noe beregning readResult.get (); Strengekko = ny streng (buffer.array ()). Trim (); buffer.clear (); retur ekko; }

5. Testen

For å bekrefte at server- og klientapplikasjonene våre fungerer i henhold til forventningene, kan vi bruke en test:

@Test offentlig ugyldig givenServerClient_whenServerEchosMessage_thenCorrect () {String resp1 = client.sendMessage ("hallo"); String resp2 = client.sendMessage ("verden"); assertEquals ("hei", resp1); assertEquals ("verden", resp2); }

6. Konklusjon

I denne artikkelen har vi utforsket Java NIO.2 asynkrone socket-kanal-API-er. Vi har vært i stand til å gå gjennom prosessen med å bygge en server og klient med disse nye API-ene.

Du kan få tilgang til hele kildekoden for denne artikkelen i Github-prosjektet.


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