Programmering

Socket programmering i Java: En tutorial

Denne tutorial er en introduktion til socket-programmering i Java, der starter med et simpelt klientservereksempel, der demonstrerer de grundlæggende funktioner i Java I / O. Du bliver introduceret til både originalenjava.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 ().

download Download kildekoden Kildekode til "Socket programmering i Java: En tutorial." Oprettet af Steven Haines til JavaWorld.

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:

  1. Opret et stik til webserveren, der lytter på port 80.
  2. Få en PrintStream til serveren og send anmodningen FÅ PATH HTTP / 1.0, hvor STI er den ønskede ressource på serveren. For eksempel, hvis vi ønskede at åbne roden på et websted, ville stien være /.
  3. Få en InputStream til serveren, pakk den med en BufferedReader 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 , STOLPE, SÆTTE, SLET, HOVED, eller MULIGHEDER. Hver metode har sin egen syntaks. Som vist i ovenstående kodestykker, bliver 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æserog pakket det derefter ind i en BufferedReader. Vi brugte PrintStream at udføre vores 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 testside

Succes

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:

  1. Lave en ServerSocket, angiver en port at lytte til.
  2. Påkald ServerSocket's acceptere() metode til at lytte på den konfigurerede port til en klientforbindelse.
  3. Når en klient opretter forbindelse til serveren, acceptere() metode returnerer a Stikkontakt hvorigennem serveren kan kommunikere med klienten. Dette er det samme Stikkontakt klasse, som vi brugte til vores klient, så processen er den samme: skaff en InputStream at læse fra klienten og en OutputStream skriv til klienten.
  4. 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.
  5. Ring til ServerSocket's acceptere() 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 ServerSocketFactorys 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 (); }}} 
$config[zx-auto] not found$config[zx-overlay] not found