java.io
pakke og NIO, den ikke-blokerende I / O (java.nio
) API'er introduceret 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 bruges til forskellige formål, og begge har unikke begrænsninger:
- TCP er relativt enkel og pålidelig protokol, der gør det muligt for en klient at oprette forbindelse til en server og de to systemer til at kommunikere. I TCP ved hver enhed, at dens kommunikationsnyttelast er modtaget.
- UDP er en forbindelsesfri protokol og er godt til scenarier, hvor du ikke nødvendigvis har brug for hver pakke for at nå frem til sin destination, såsom mediestreaming.
For at forstå forskellen mellem TCP og UDP skal du overveje, hvad der ville ske, hvis du streamede video fra dit yndlingswebsted, og det faldt rammer. Foretrækker du, at klienten sænker din film for at modtage de manglende rammer, eller foretrækker du, at videoen fortsætter med at afspilles? Protokoller til videostreaming udnytter typisk UDP. Da TCP garanterer levering, er det den valgte protokol for HTTP, FTP, SMTP, POP3 osv.
I denne vejledning introducerer jeg dig til socket-programmering i Java. Jeg præsenterer en række klientservereksempler, der demonstrerer funktioner fra den originale Java I / O-ramme og derefter gradvist går videre til brug af funktioner, der er introduceret i NIO.2.
Old-school Java-stik
I implementeringer forud for NIO håndteres Java TCP-klient-sokkelkode af java.net.Socket
klasse. Følgende kode åbner en forbindelse til en server:
Stikkontakt = ny stikkontakt (server, port);
En gang vores stikkontakt
forekomst er forbundet til serveren, vi kan begynde at hente input og output streams til severen. 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 for at opnå input og output streams:
InputStream in = socket.getInputStream (); OutputStream out = socket.getOutputStream ();
Da dette er almindelige streams, de samme streams, som vi ville bruge til at læse fra og skrive til en fil, kan vi konvertere dem til den form, der bedst tjener vores brugssag. For eksempel kunne vi pakke OutputStream
med en PrintStream
så vi let kan skrive tekst med metoder som println ()
. For et andet eksempel kunne vi pakke InputStream
med en BufferedReader
, via en InputStreamReader
, for let at læse tekst med metoder som readLine ()
.
Java-socket-klienteksempel
Lad os gennemgå 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 den enkleste sag: anmode om en ressource fra serveren, og serveren returnerer svaret og lukker strømmen. Denne sag kræver følgende trin:
- Opret et stik til webserveren, der lytter på port 80.
- Få en
PrintStream
til serveren og send anmodningenFÅ PATH HTTP / 1.0
, hvorSTI
er den ønskede ressource på serveren. For eksempel, hvis vi ønskede at åbne roden på et websted, ville stien være/
. - Få en
InputStream
til serveren, pakk den med enBufferedReader
og læs svaret linie for linje.
Liste 1 viser kildekoden til dette eksempel.
Notering 1. SimpleSocketClientExample.java
pakke com.geekcap.javaworld.simplesocketclient; importere java.io.BufferedReader; importere java.io.InputStreamReader; import java.io.PrintStream; import java.net.Socket; offentlig klasse SimpleSocketClientExample {public static void main (String [] args) {if (args.length <2) {System.out.println ("Usage: SimpleSocketClientExample"); System.exit (0); } Stringserver = args [0]; Strengsti = args [1]; System.out.println ("Indlæser indhold af URL:" + server); prøv {// Opret forbindelse til serveren Socket-bøsning = ny Socket (server, 80); // Opret input- og outputstrømme til at læse fra og skrive til serveren PrintStream out = ny PrintStream (socket.getOutputStream ()); BufferedReader in = ny BufferedReader (ny InputStreamReader (socket.getInputStream ())); // Følg HTTP-protokollen for GET HTTP / 1.0 efterfulgt af en tom linje ud. Println ("GET" + sti + "HTTP / 1.0"); out.println (); // Læs data fra serveren, indtil vi er færdige med at læse dokumentet String line = in.readLine (); mens (linje! = null) {System.out.println (linje); line = in.readLine (); } // Luk vores streams in.close (); out.close (); socket.close (); } fange (Undtagelse e) {e.printStackTrace (); }}}
Liste 1 accepterer to kommandolinjeargumenter: den server, der skal oprettes forbindelse til (forudsat at vi opretter forbindelse til serveren på port 80) og den ressource, der skal hentes. Det skaber en Stikkontakt
der peger på serveren og specifikt specificerer port 80
. Derefter udfører kommandoen:
FÅ PATH HTTP / 1.0
For eksempel:
GET / HTTP / 1.0
Hvad skete der lige?
Når du henter en webside fra en webserver, f.eks www.google.com
, HTTP-klienten bruger DNS-servere til at finde serverens adresse: det starter med at bede topdomæneserveren om com
domæne, hvor den autoritative domænenavnsserver er til www.google.com
. Derefter beder den domænenavnsserver om IP-adressen (eller adresserne) til www.google.com
. Dernæst åbner det 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 udfører HTTP-klienten den angivne HTTP-metode, f.eks FÅ
, STOLPE
, SÆTTE
, SLET
, HOVED
, eller MULIGHEDER
. Hver metode har sin egen syntaks. Som vist i ovenstående kodestykker, bliver FÅ
metode kræver en sti efterfulgt af HTTP / versionsnummer
og en tom linje. Hvis vi ønskede at tilføje HTTP-overskrifter, kunne vi have gjort det inden vi kom ind i den nye linje.
I liste 1 hentede vi en OutputStream
og pakket det i en PrintStream
så vi lettere kunne udføre vores tekstbaserede kommandoer. Vores kode fik en InputStream
, pakket det i en InputStreamReader
, der konverterede den til en Læser
og pakket det derefter ind i en BufferedReader
. Vi brugte PrintStream
at udføre vores FÅ
metode og derefter brugt BufferedReader
at læse svaret linje for linje, indtil vi modtog en nul
svar, hvilket indikerer, at stikkontakten var lukket.
Udfør nu denne klasse og send følgende argumenter:
java com.geekcap.javaworld.simplesocketclient.SimpleSocketClientExample www.javaworld.com /
Du skal se output svarende til hvad der er nedenfor:
Indlæser indhold af URL: www.javaworld.com HTTP / 1.1 200 OK Dato: søn, 21. september 2014 22:20:13 GMT Server: Apache X-Gas_TTL: 10 Cache-kontrol: max-age = 10 X-GasHost: gas2 .usw X-Cooking-With: Benzin-Local X-Benzin-Alder: 8 Indholdslængde: 168 Sidst ændret: Tir, 24. jan 2012 00:09:09 GMT Etag: "60001b-a8-4b73af4bf3340" Indholdstype : text / html Varier: Accept-kodende forbindelse: luk benzin testsideSucces
Denne output viser en testside på JavaWorlds hjemmeside. Det svarede tilbage, at det taler HTTP version 1.1, og svaret er 200 OK
.
Eksempel på Java-sokkelserver
Vi har dækket klientsiden, og heldigvis er kommunikationsaspektet på serversiden lige så let. Fra et forenklet perspektiv er processen som følger:
- Lave en
ServerSocket
, angiver en port at lytte til. - Påkald
ServerSocket
'sacceptere()
metode til at lytte på den konfigurerede port til en klientforbindelse. - Når en klient opretter forbindelse til serveren,
acceptere()
metode returnerer aStikkontakt
hvorigennem serveren kan kommunikere med klienten. Dette er det sammeStikkontakt
klasse, som vi brugte til vores klient, så processen er den samme: skaff enInputStream
at læse fra klienten og enOutputStream
skriv til klienten. - Hvis din server skal være skalerbar, vil du videresende den
Stikkontakt
til en anden tråd, der skal behandles, så din server kan fortsætte med at lytte efter yderligere forbindelser. - Ring til
ServerSocket
'sacceptere()
metode igen for at lytte efter en anden forbindelse.
Som du snart vil se, ville NIO's håndtering af dette scenarie være lidt anderledes. For nu kan vi dog direkte oprette en ServerSocket
ved at give den en port til at lytte på (mere om ServerSocketFactory
s i næste afsnit):
ServerSocket serverSocket = ny ServerSocket (port);
Og nu kan vi acceptere indgående forbindelser via acceptere()
metode:
Socket-sokkel = serverSocket.accept (); // Håndter forbindelsen ...
Multitrådet programmering med Java-sockets
Listing 2 nedenfor sætter al serverkoden så langt sammen til et lidt mere robust eksempel, der bruger tråde til at håndtere flere anmodninger. Den viste server er en ekkoserver, hvilket betyder, at den ekko tilbage enhver besked, den modtager.
Mens eksemplet i Listing 2 ikke er kompliceret, forventer det noget af, hvad der kommer op i det næste afsnit om NIO. Vær særlig opmærksom på mængden af trådkode, vi skal skrive for at opbygge en server, der kan håndtere flere samtidige anmodninger.
Notering 2. SimpleSocketServer.java
pakke com.geekcap.javaworld.simplesocketclient; importere java.io.BufferedReader; importere java.io.I / OException; importere java.io.InputStreamReader; import java.io.PrintWriter; importere java.net.ServerSocket; import java.net.Socket; offentlig klasse SimpleSocketServer udvider tråd {privat ServerSocket serverSocket; privat int port; privat boolsk løb = falsk; offentlig SimpleSocketServer (int-port) {this.port = port; } offentlig ugyldig startServer () {prøv {serverSocket = ny ServerSocket (port); this.start (); } fange (I / OException e) {e.printStackTrace (); }} offentlig ugyldig stopServer () {running = false; this.interrupt (); } @Override public void run () {running = true; mens (kører) {prøv {System.out.println ("Lytter efter en forbindelse"); // Ring til accept () for at modtage den næste forbindelse Socket-stikkontakt = serverSocket.accept (); // Send soklen til RequestHandler-tråden til behandling af RequestHandler requestHandler = ny RequestHandler (sokkel); requestHandler.start (); } fange (I / OException e) {e.printStackTrace (); }}} offentlig statisk ugyldig hoved (String [] args) {if (args.length == 0) {System.out.println ("Usage: SimpleSocketServer"); System.exit (0); } int port = Integer.parseInt (args [0]); System.out.println ("Start server på port:" + port); SimpleSocketServer-server = ny SimpleSocketServer (port); server.startServer (); // Automatisk nedlukning på 1 minut, prøv {Thread.sleep (60000); } fange (Undtagelse e) {e.printStackTrace (); } server.stopServer (); }} klasse RequestHandler udvider tråd {privat stikkontakt; RequestHandler (stikkontakt) {this.socket = stikkontakt; } @ Override public void run () {prøv {System.out.println ("Modtaget en forbindelse"); // Få input og output streams BufferedReader in = new BufferedReader (new InputStreamReader (socket.getInputStream ())); PrintWriter out = ny PrintWriter (socket.getOutputStream ()); // Skriv vores overskrift ud til klienten out.println ("Echo Server 1.0"); out.flush (); // Echo linjer tilbage til klienten, indtil klienten lukker forbindelsen, eller vi modtager en tom linje String line = in.readLine (); mens (line! = null && line.length ()> 0) {out.println ("Echo:" + line); out.flush (); line = in.readLine (); } // Luk vores forbindelse in.close (); out.close (); socket.close (); System.out.println ("Forbindelse lukket"); } fange (Undtagelse e) {e.printStackTrace (); }}}