Programmering

Socket programmering i Java: En veiledning

Denne opplæringen er en introduksjon til socket-programmering i Java, og starter med et enkelt klientservereksempel som viser de grunnleggende funksjonene til Java I / O. Du blir introdusert for både originalenjava.io pakke og NIO, den ikke-blokkerende I / O (java.nio) API-er introdusert i Java 1.4. Til slutt ser du et eksempel som demonstrerer Java-nettverk som implementert fra Java 7 fremover, i NIO.2.

Socket programmering koker ned til to systemer som kommuniserer med hverandre. Vanligvis kommer nettverkskommunikasjon i to smaker: Transport Control Protocol (TCP) og User Datagram Protocol (UDP). TCP og UDP brukes til forskjellige formål, og begge har unike begrensninger:

  • TCP er relativt enkel og pålitelig protokoll som gjør det mulig for en klient å koble til en server og de to systemene for å kommunisere. I TCP vet hver enhet at dens kommunikasjonsnyttelast er mottatt.
  • UDP er en tilkoblingsfri protokoll og er bra for scenarier der du ikke nødvendigvis trenger hver pakke for å komme til destinasjonen, for eksempel mediestreaming.

For å forstå forskjellen mellom TCP og UDP, bør du vurdere hva som ville skje hvis du strømmet video fra favorittnettstedet ditt og det droppet bilder. Foretrekker du at klienten bremser filmen for å motta de manglende bildene, eller foretrekker du at videoen fortsetter å spilles av? Protokoller for videostreaming utnytter vanligvis UDP. Fordi TCP garanterer levering, er det den valgte protokollen for HTTP, FTP, SMTP, POP3 og så videre.

I denne veiledningen introduserer jeg deg for socket-programmering i Java. Jeg presenterer en rekke klientservereksempler som viser funksjoner fra det opprinnelige Java I / O-rammeverket, og deretter gradvis videre til bruk av funksjoner introdusert i NIO.2.

Old-school Java-stikkontakter

I implementeringer før NIO håndteres Java TCP-klientkontaktkoden av java.net.Socket klasse. Følgende kode åpner en forbindelse til en server:

 Stikkontakt = ny stikkontakt (server, port); 

En gang vår stikkontakt forekomst er koblet til serveren, vi kan begynne å få inn- og utgangsstrømmer til severen. Inngangsstrømmer brukes til å lese data fra serveren mens utgangsstrømmer brukes til å skrive data til serveren. Vi kan utføre følgende metoder for å få inn- og utgangsstrømmer:

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

Fordi dette er vanlige strømmer, de samme strømmer som vi vil bruke til å lese fra og skrive til en fil, kan vi konvertere dem til det skjemaet som best tjener brukssaken vår. For eksempel kan vi pakke inn OutputStream med en PrintStream slik at vi enkelt kan skrive tekst med metoder som println (). For et annet eksempel kan vi pakke inn InputStream med en BufferedReader, via en InputStreamReader, for enkelt å lese tekst med metoder som readLine ().

last ned Last ned kildekoden Kildekoden for "Socket-programmering i Java: En opplæring." Skapt av Steven Haines for JavaWorld.

Java socket klienteksempel

La oss jobbe gjennom et kort eksempel som kjører en HTTP GET mot en HTTP-server. HTTP er mer sofistikert enn eksemplet vårt tillater, men vi kan skrive klientkode for å håndtere den enkleste saken: be om en ressurs fra serveren, og serveren returnerer svaret og lukker strømmen. Denne saken krever følgende trinn:

  1. Opprett en stikkontakt til webserveren som lytter på port 80.
  2. Få en PrintStream til serveren og send forespørselen FÅ PATH HTTP / 1.0, hvor STI er den etterspurte ressursen på serveren. For eksempel, hvis vi ønsket å åpne roten til et nettsted, ville banen være /.
  3. Få en InputStream til serveren, pakk den inn med en BufferedReader og les svaret linje for linje.

Oppføring 1 viser kildekoden for dette eksemplet.

Oppføring 1. SimpleSocketClientExample.java

pakke com.geekcap.javaworld.simplesocketclient; importere java.io.BufferedReader; importere java.io.InputStreamReader; importer java.io.PrintStream; importere java.net.Socket; public class SimpleSocketClientExample {public static void main (String [] args) {if (args.length <2) {System.out.println ("Bruk: SimpleSocketClientExample"); System.exit (0); } Strengserver = args [0]; Strengsti = args [1]; System.out.println ("Laster innhold av URL:" + server); prøv {// Koble til serveren Socket socket = new Socket (server, 80); // Lag inngangs- og utgangsstrømmer for å lese fra og skrive til serveren PrintStream out = new PrintStream (socket.getOutputStream ()); BufferedReader in = new BufferedReader (new InputStreamReader (socket.getInputStream ())); // Følg HTTP-protokollen til GET HTTP / 1.0 etterfulgt av en tom linje ut. Println ("GET" + sti + "HTTP / 1.0"); out.println (); // Les data fra serveren til vi er ferdig med å lese dokumentet String line = in.readLine (); mens (linje! = null) {System.out.println (linje); linje = in.readLine (); } // Lukk strømmen vår i. Lukk (); ut. lukk (); socket.close (); } fange (Unntak e) {e.printStackTrace (); }}} 

Oppføring 1 godtar to kommandolinjeargumenter: serveren å koble til (forutsatt at vi kobler til serveren på port 80) og ressursen som skal hentes. Det skaper en Stikkontakt som peker til serveren og spesifiserer port 80. Den utfører deretter kommandoen:

FÅ PATH HTTP / 1.0 

For eksempel:

GET / HTTP / 1.0 

Hva skjedde nå?

Når du henter en webside fra en webserver, for eksempel www.google.com, bruker HTTP-klienten DNS-servere for å finne serverens adresse: det begynner med å be toppdomeneserveren om com domenet der den autoritative domenenavnsserveren er for www.google.com. Så ber den domenenavnsserveren for IP-adressen (eller adressene) for www.google.com. Deretter åpner den en stikkontakt til den serveren på port 80. (Eller hvis du vil definere en annen port, kan du gjøre det ved å legge til et kolon etterfulgt av portnummeret, for eksempel: :8080.) Til slutt kjører HTTP-klienten den spesifiserte HTTP-metoden, for eksempel , POST, SETTE, SLETT, HODE, eller OPTI / ONS. Hver metode har sin egen syntaks. Som vist i kodene ovenfor, blir metoden krever en bane fulgt av HTTP / versjonsnummer og en tom linje. Hvis vi ønsket å legge til HTTP-overskrifter, kunne vi ha gjort det før vi gikk inn i den nye linjen.

I oppføring 1 hentet vi en OutputStream og pakket den inn i en PrintStream slik at vi lettere kunne utføre våre tekstbaserte kommandoer. Koden vår fikk en InputStream, pakket det inn i en InputStreamReader, som konverterte den til en Leser, og pakket den deretter inn i en BufferedReader. Vi brukte PrintStream å utføre vår metode og deretter brukt BufferedReader å lese svaret linje for linje til vi fikk en null svar, noe som indikerer at stikkontakten var lukket.

Utfør nå denne klassen og gi den følgende argumenter:

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

Du bør se utdata som ligner på det som er nedenfor:

Laster innhold av URL: www.javaworld.com HTTP / 1.1 200 OK Dato: Sun, 21 Sep 2014 22:20:13 GMT Server: Apache X-Gas_TTL: 10 Cache-Control: max-age = 10 X-GasHost: gas2 .usw X-Cooking-With: Bensin-Lokal X-Bensin-Alder: 8 Innholdslengde: 168 Sist endret: Tirsdag, 24. januar 2012 00:09:09 GMT Etag: "60001b-a8-4b73af4bf3340" Innholdstype : text / html Varier: Godta-kodingstilkobling: lukk bensintestside

Suksess

Denne utgangen viser en testside på JavaWorlds nettsted. Det svarte tilbake at det snakker HTTP versjon 1.1 og svaret er 200 OK.

Eksempel på Java-sokkelserver

Vi har dekket klientsiden, og heldigvis er kommunikasjonsaspektet på serversiden like enkelt. Fra et forenklet perspektiv er prosessen som følger:

  1. Lage en ServerSocket, spesifiserer en port å lytte til.
  2. Påkalle ServerSockets aksepterer() metode for å lytte på den konfigurerte porten for en klientforbindelse.
  3. Når en klient kobler seg til serveren, blir aksepterer() metoden returnerer a Stikkontakt som serveren kan kommunisere med klienten gjennom. Dette er det samme Stikkontakt klasse som vi brukte for vår klient, så prosessen er den samme: skaff en InputStream å lese fra klienten og en OutputStream skrive til klienten.
  4. Hvis serveren din skal være skalerbar, vil du passere Stikkontakt til en annen tråd for å behandle slik at serveren din kan fortsette å lytte etter flere tilkoblinger.
  5. Ring ServerSockets aksepterer() metoden igjen for å lytte etter en annen forbindelse.

Som du snart vil se, vil NIOs håndtering av dette scenariet være litt annerledes. For nå kan vi imidlertid direkte lage en ServerSocket ved å gi den en port å lytte til (mer om ServerSocketFactorys i neste avsnitt):

 ServerSocket serverSocket = ny ServerSocket (port); 

Og nå kan vi godta innkommende forbindelser via aksepterer() metode:

 Stikkontakt = serverSocket.accept (); // Håndter tilkoblingen ... 

Flertrådet programmering med Java-stikkontakter

Oppføring 2 nedenfor setter all serverkoden så langt sammen til et litt mer robust eksempel som bruker tråder til å håndtere flere forespørsler. Serveren som vises er en ekkoserver, noe som betyr at den gir ekko tilbake enhver melding den mottar.

Selv om eksemplet i Listing 2 ikke er komplisert, forventer det noe av det som kommer opp i neste avsnitt om NIO. Vær spesielt oppmerksom på hvor mye trådkode vi må skrive for å bygge en server som kan håndtere flere samtidige forespørsler.

Oppføring 2. SimpleSocketServer.java

pakke com.geekcap.javaworld.simplesocketclient; importere java.io.BufferedReader; importere java.io.I / OException; importere java.io.InputStreamReader; importere java.io.PrintWriter; importere java.net.ServerSocket; importere java.net.Socket; offentlig klasse SimpleSocketServer utvider tråd {privat ServerSocket serverSocket; privat int port; privat boolsk løping = 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 (kjører) {prøv {System.out.println ("Lytter etter en tilkobling"); // Call accept () for å motta neste tilkobling Socket socket = serverSocket.accept (); // Gi kontakten til RequestHandler-tråden for behandling av RequestHandler requestHandler = ny RequestHandler (socket); requestHandler.start (); } fange (I / OException e) {e.printStackTrace (); }}} offentlig statisk ugyldig hoved (String [] args) {if (args.length == 0) {System.out.println ("Bruk: 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 (); // Slå av automatisk på 1 minutt, prøv {Thread.sleep (60000); } fange (Unntak e) {e.printStackTrace (); } server.stopServer (); }} klasse RequestHandler utvider tråd {privat stikkontakt; RequestHandler (stikkontakt) {this.socket = stikkontakt; } @ Override public void run () {prøv {System.out.println ("Mottatt en tilkobling"); // Få inngangs- og utgangsstrømmer BufferedReader i = ny BufferedReader (ny InputStreamReader (socket.getInputStream ())); PrintWriter out = ny PrintWriter (socket.getOutputStream ()); // Skriv ut overskriften vår til klienten out.println ("Echo Server 1.0"); ut.flush (); // Ekkolinjer tilbake til klienten til klienten lukker forbindelsen eller vi mottar en tom linje String line = in.readLine (); while (line! = null && line.length ()> 0) {out.println ("Echo:" + line); ut.flush (); linje = in.readLine (); } // Lukk forbindelsen vår i. Lukk (); ut. lukk (); socket.close (); System.out.println ("Forbindelse lukket"); } fange (Unntak e) {e.printStackTrace (); }}} 
$config[zx-auto] not found$config[zx-overlay] not found