Socket programmering i Java tutorial

Denne vejledning er en introduktion til socket programmering i Java, der starter med et enkelt klient-server eksempel viser de grundlæggende funktioner i Java i/O. Du vil blive introduceret til både den oprindelige java.io pakke og NIO, de ikke-blokerende I/O (java.nio) Api ‘ er, der blev indført i Java 1.4. Endelig vil du se et eksempel, der demonstrerer Java-netværk som implementeret fra Java 7 fremad, i NIO.2.,

Socket programmering koger ned til to systemer, der kommunikerer med hinanden. Generelt kommer netværkskommunikation i to varianter: Transport Control Protocol (TCP) og User Datagram Protocol (UDP). TCP og UDP anvendes til forskellige formål, og begge har særlige begrænsninger:

  • TCP er forholdsvis enkel og pålidelig protokol, der gør det muligt for en klient til at oprette forbindelse til en server og de to systemer til at kommunikere. I TCP ved hver enhed, at dens kommunikationsgevinster er modtaget.,
  • UDP er en forbindelsesløs protokol og er god til scenarier, hvor du ikke nødvendigvis har brug for hver pakke for at nå frem til dens destination, såsom mediestreaming.

for at værdsætte forskellen mellem TCP og UDP skal du overveje, hvad der ville ske, hvis du streamede video fra dit yndlings websiteebsted, og det faldt rammer. Foretrækker du, at klienten bremser din film for at modtage de manglende rammer, eller foretrækker du, at videoen fortsætter med at afspille? Video streaming protokoller typisk udnytte UDP., Fordi TCP garanterer levering, er det den valgte protokol for HTTP, FTP, SMTP, POP3 og så videre.

i denne tutorial introducerer jeg dig til socket programmering i Java. Jeg præsenterer en række klient-servereksempler, der demonstrerer funktioner fra den originale Java I/O-ramme og derefter gradvist går videre til at bruge funktioner introduceret i NIO.2.

Old-school Java sockets

i implementeringer før NIO håndteres Java TCP client socket code af java.net.Socket klassen., Følgende kode åbner en forbindelse til en server:

Socket socket = new Socket( server, port );

Når vores socket instans er forbundet til serveren, vi kan begynde at få input og output streams til sever. Input streams bruges til at læse data fra serveren, mens output streams bruges til at skrive data til serveren., Vi kan udføre følgende metoder til at få input og output streams:

InputStream in = socket.getInputStream();OutputStream out = socket.getOutputStream();

Fordi disse er almindelige vandløb, samme vandløb, som vi vil bruge til at læse fra og skrive til en fil, kan vi konvertere dem til den form, der bedst tjener vores use case. For eksempel, vi kunne wrap OutputStream med PrintStream, så vi nemt kan skrive tekst med metoder som println()., For et andet eksempel, vi kunne wrap InputStream med BufferedReader, via en InputStreamReader, så det er let at læse tekst med metoder som readLine().

do .nload

kildekode til “Socket programmering i Java: En tutorial.”Oprettet af Steven Haines til Java .orld .

Java socket klient eksempel

Lad os arbejde gennem et kort eksempel, der udfører en HTTP GET mod en HTTP-server., HTTP er mere sofistikeret end vores eksempel tillader, men vi kan skrive klientkode for at håndtere det enkleste tilfælde: Anmod om en ressource fra serveren, og serveren returnerer svaret og lukker strømmen. Denne sag kræver følgende trin:

  1. Opret en stikkontakt til listeningebserveren, der lytter på port 80.
  2. Få en PrintStream til serveren og sende anmodningen GET PATH HTTP/1.0, hvor PATH den anmodede ressource på serveren., For eksempel, hvis vi ønskede at åbne roden til et webebsted, ville stien være /.
  3. Få en InputStream til serveren, afslutte det med en BufferedReader og læs svaret line-by-line.

notering 1 viser kildekoden til dette eksempel.

liste 1. Simplesocketclienteamample.java

Listing 1 accepterer to kommandolinjeargumenter: serveren, der skal oprettes forbindelse til (forudsat at vi opretter forbindelse til serveren på port 80) og ressourcen, der skal hentes., Det skaber en Socket, der peger på serveren og udtrykkeligt angiver port 80. Derefter udfører kommandoen:

GET PATH HTTP/1.0

For eksempel:

GET / HTTP/1.0

Hvad skete der lige?

Når du henter en webside fra en web-server, som f.eks. , HTTP-klient bruger DNS-servere for at finde serverens adresse: det starter med at spørge top-level domæne-server com domæne, hvor den autoritative domæne-navn server er for ., Derefter beder den om, at domænenavnsserver til IP-adressen (eller adresserne) for . Dernæst åbner den en stikkontakt til den server på port 80. (Eller hvis du vil definere en anden port, kan du gøre det ved at tilføje et kolon efterfulgt af portnummeret, for eksempel: :8080. Endelig, HTTP-klient udfører den angivne HTTP metode, som f.eks. GET POST PUT DELETE HEAD, eller OPTI/ONS. Hver metode har sin egen syntaks., Som vist i ovenstående kode klip, GET metode kræver en sti efterfulgt af HTTP/version number og en tom linje. Hvis vi ønskede at tilføje HTTP-overskrifter, kunne vi have gjort det, før vi kom ind i den nye linje.

I Oversigt 1, vi hentet en OutputStream og svøbte det i et PrintStream, så vi lettere kunne udføre vores tekstbaserede kommandoer., Vores kode opnået en InputStream, pakket ind i en InputStreamReader, som omdannes til en Reader, og derefter pakket ind, at der i en BufferedReader. Vi brugte PrintStream for at udføre vores GET metode, og derefter brugte BufferedReader for at læse det svar, line-by-line, før vi har modtaget en null svar, der angiver, at soklen var blevet lukket.,

Nu udføre denne klasse, og videregive det følgende argumenter:

java com.geekcap.javaworld.simplesocketclient.SimpleSocketClientExample www.javaworld.com /

Du bør se output svarer til, hvad der er nedenfor:

Denne udgang viser en test side på JavaWorld ‘ s hjemmeside. Det svarede tilbage, at det taler HTTP version 1.1 og svaret er 200 OK.

Java socket server eksempel

Vi har dækket klientsiden, og heldigvis er kommunikationsaspektet på serversiden lige så let., Fra et forenklet perspektiv er processen som følger:

  1. Opret enServerSocket, angiv en port, der skal lyttes til.
  2. påkald ServerSocket‘s accept() metode til at lytte på den konfigurerede port til en klientforbindelse.
  3. når en klient opretter forbindelse til serveren, returnerer accept() metoden en Socket gennem hvilken serveren kan kommunikere med klienten., Dette er det samme Socket class, som vi har brugt til vores klient, så processen er den samme: at få en InputStream for at læse fra klienten og en OutputStream skriv til kunden.
  4. hvis din server skal være skalerbar, vil du sende Socket til en anden tråd, der skal behandles, så din server kan fortsætte med at lytte efter yderligere forbindelser.
  5. Opkald > ServerSocket‘s accept() metode igen for at lytte til en anden forbindelse.,

som du snart vil se, ville NIOS håndtering af dette scenario være lidt anderledes., For nu, selvom vi direkte kan oprette en ServerSocket ved at overføre det til en port til at lytte til (mere om ServerSocketFactorys i næste afsnit):

ServerSocket serverSocket = new ServerSocket( port );

Og nu kan vi acceptere indgående forbindelser via accept() metode:

Socket socket = serverSocket.accept();// Handle the connection ...

Multitrådet programmering med Java stikkontakter

Liste 2, nedenfor, sætter alle server-kode, så langt sammen ind i en lidt mere robust eksempel, der bruger tråde til at håndtere flere anmodninger., Den viste server er en echo-server, hvilket betyder, at den gentager enhver meddelelse, den modtager.

mens eksemplet i Liste 2 ikke er kompliceret, forventer det noget af, hvad der kommer op i næste afsnit om NIO. Vær særlig opmærksom på mængden af gevindkode, vi skal skrive for at opbygge en server, der kan håndtere flere samtidige anmodninger.

Liste 2. SimpleSocketServer.java

I Liste 2 opretter vi en ny SimpleSocketServer forekomst og starter serveren., Dette er påkrævet fordi SimpleSocketServer udvider Thread for at oprette en ny tråd til at håndtere blokering accept() opkald, som du kan se i read() metode. Metoden run() sidder i en løkke, der accepterer klientanmodninger og opretter RequestHandler tråde for at behandle anmodningen. Igen er dette relativt simpel kode, men involverer også en hel del gevindprogrammering.,

Bemærk at RequestHandler varetager klienten kommunikation meget gerne koden i eksempel 1 har: det wraps OutputStream med PrintStream for at gøre det nemt at skriver, og på samme måde, wraps InputStream med BufferedReader for nem læser. Så vidt en server går, læser den linjer fra klienten og gentager dem tilbage til klienten. Hvis klienten sender en tom linje, er samtalen forbi, og RequestHandler lukker stikket.

NIO og NIO.,2

For mange applikationer er Basisprogrammeringsmodellen Java socket, som vi lige har udforsket, tilstrækkelig. For applikationer, der involverer mere intensiv I/O eller asynkron input / output, vil du være bekendt med de ikke-blokerende API ‘ er, der blev introduceret i Java NIO og NIO.2.

JDK 1.4 NIO-pakken tilbyder følgende nøglefunktioner:

  • kanaler er designet til at understøtte bulkoverførsler fra en NIO-buffer til en anden.
  • buffere repræsenterer en sammenhængende blok af hukommelse, der er forbundet med et simpelt sæt operationer.,
  • ikke-blokerende Input/Output er et sæt klasser, der udsætter kanaler for almindelige i / O-kilder som filer og stikkontakter.

Når du programmerer med NIO, åbner du en kanal til din destination og læser derefter data i en buffer fra destinationen, skriver dataene til en buffer og sender dem til din destination., Vi vil dykke ind i at oprette en socket og få en kanal til den om lidt, men først lad os gennemgå processen med at bruge en buffer:

  1. Skriv data i en buffer
  2. Ring buffer ‘ s flip() metode til at forberede det for læsning
  3. Læs data fra buffer
  4. Ring buffer ‘ s clear() eller compact() metode til at forberede den til at modtage flere data

Når data er skrevet ind i den buffer, den buffer, der kender mængden af data, der skrives ind i det., Det fastholder tre egenskaber, hvis betydning varierer, hvis den buffer, der er i læse-tilstand eller skrive-tilstand:

  • Position: I skrive-tilstand, den oprindelige position er 0, og det holder den aktuelle position bliver skrevet i den buffer, efter du trykker på en buffer til at sætte det i læse tilstand, det nulstiller den holdning, at 0 og holder den aktuelle position i den buffer, som læses fra,
  • Kapacitet: Fast størrelse buffer
  • Limit: I skrive-tilstand, den grænse, der definerer, hvor meget data der kan skrives ind i en buffer; i læse tilstand, den grænse, der definerer, hvor meget data kan læses fra bufferen.,

Java I / O demo: Echo server med NIO.2

NIO.2, som blev indført i JDK 7, udvider Java ‘ s ikke-blokerende i/O-biblioteker for at tilføje understøttelse af filsystemet opgaver, såsom java.nio.file pakke og java.nio.file.Path class og afslører en ny Fil System-API ‘ en. Med den baggrund i tankerne, lad os skrive en ny Echo-Server ved hjælp af NIO.2 ‘er AsynchronousServerSocketChannel.

AsynchronousServerSocketChannel giver en ikke-blokerende asynkron kanal for stream-orienteret lytte stikkontakter., For at bruge det, vi først udføre sin statiske open() metode, og derefter bind() det til en bestemt port. Dernæst udfører vi dens accept() metode, der giver den en klasse, der implementerer CompletionHandler interface. Oftest vil du opdage, at handler oprettet som en anonym indre klasse.

notering 3 viser kildekoden til vores nye Asynchronous Echo Server.

Liste 3. SimpleSocketServer.,java

I Oversigt 3 skal vi først oprette et nyt AsynchronousServerSocketChannel og derefter binde det til port 5000:

 final AsynchronousServerSocketChannel listener = AsynchronousServerSocketChannel.open().bind(new InetSocketAddress(5000));

denne AsynchronousServerSocketChannel, vi påberåbe accept() for at fortælle det, til at starte med at lytte til forbindelser, passerer det en brugerdefineret CompletionHandler eksempel. Når vi påberåber accept(), vender den straks tilbage., Bemærk, at dette eksempel er forskellige fra ServerSocket class i Oversigt 1; der henviser til, at accept() metode blokeret, indtil en klient tilsluttes til det, den AsynchronousServerSocketChannel accept() metode klarer det for os.

afslutningen handling

Vores næste opgave er at oprette en CompletionHandler class og give en gennemførelse af completed() og failed() metoder., completed() metode kaldes, når AsynchronousServerSocketChannel modtager en forbindelse fra en klient, og den indeholder en AsynchronousSocketChannel til kunden. completed() – metoden accepterer først forbindelsen fra AsynchronousServerSocketChannel og begynder derefter at kommunikere med klienten. Den første ting, som det gør, er at skrive “Hej” besked: Det bygger en streng, konverterer det til et byte array, og derefter sender det til ByteBuffer.wrap() for at konstruere en ByteBuffer., ByteBuffer kan derefter blive videregivet AsynchronousSocketChannel‘s write() metode.

for At læse fra kunden, vi opret et nyt ByteBuffer ved at påberåbe sig sin allocate(4096) (som skaber en 4K-buffer), så må vi påberåbe AsynchronousSocketChannel‘s read() metode. read() returnerer en Future<Integer>, som vi kan kalde get() for at hente antallet af bytes, der læses fra kunden., I dette eksempel, vi passerer get() timeout-værdi på 20 sekunder: hvis vi ikke får svar på i 20 sekunder, hvorefter get() metode vil smide en TimeoutException. Vores regel for denne echo-server er, at hvis vi observerer 20 sekunders stilhed, afslutter vi samtalen.

næste kontrollerer vi placeringen af bufferen, som vil være placeringen af den sidste byte modtaget fra klienten. Hvis klienten sender en tom linje, modtager vi to byte: en vognretur og en linjefeed., Kontrollen sikrer, at hvis klienten sender en tom linje, tager vi den som en indikator for, at klienten er færdig med samtalen. Hvis vi har meningsfulde data, kalder viByteBuffer‘sflip() metode til at forberede den til læsning. Vi opretter et midlertidigt byte-array for at holde antallet af byte, der læses fra klienten, og derefter påkalde ByteBuffer‘s get() for at indlæse data i det byte-array. Endelig konverterer vi byte-arrayet til en streng ved at oprette en ny String forekomst., Vi echo linjen tilbage til klienten ved at konvertere strengen til et byte array, der passerer til ByteBuffer.wrap() metode og påberåber sig den AsynchronousSocketChannel‘s write() metode. Nu er vi clear() ByteBuffer, der tilbagekaldelse betyder, at det placerer de position til nul, og sætter ByteBuffer i skrive-mode, og så kan vi læse den næste linje fra kunden.,

den eneste ting at være opmærksom på er, at main() – metoden, der opretter serveren, også opretter en 60 sekunders timer for at holde applikationen kørende. Fordi AsynchronousSocketChannel ‘s accept() – metoden vender tilbage med det samme, hvis vi ikke har Thread.sleep(), stopper vores applikation øjeblikkeligt.,

for At teste det ud, starte serveren og oprette forbindelse til det ved hjælp af en telnet-klient:

telnet localhost 5000

Send et par strenge at den server, observere, at de ekkoet tilbage til dig, og derefter sende en tom linje for at afslutte samtalen.

konklusion

I denne artikel har jeg præsenteret to tilgange til socket programmering med Java: den traditionelle indfaldsvinkel, der blev indført med Java 1.0 og nyere, ikke-blokerende NIO og NIO.2 fremgangsmåder indført i Java 1.4 og Java 7, henholdsvis., Du har set flere iterationer af en Java-socket-klienten og en Java-socket-server eksempel, der viser, at både nytte af grundlæggende Java I/O og nogle scenarier, hvor ikke-blokerende i/O-forbedrer Java socket programmering model. Brug af ikke-blokerende I/O, kan du programmere Java netværksbaserede applikationer til at håndtere flere samtidige forbindelser uden at skulle administrere flere tråd samlinger. Du kan også drage fordel af den nye serverskalerbarhed, der er indbygget i NIO og NIO.2.

Skriv et svar

Din e-mailadresse vil ikke blive publiceret. Krævede felter er markeret med *