Ohjelmointi

Socket-ohjelmointi Java: ssa: opetusohjelma

Tämä opetusohjelma on johdatus Java-ohjelmointiin, aloittaen yksinkertaisesta asiakas-palvelin-esimerkistä, joka osoittaa Java I / O: n perusominaisuudet. Sinulle esitellään molemmat alkuperäisetjava.io paketti ja NIO, estämätön I / O (java.nio) Java 1.4: ssä esitetyt sovellusliittymät. Lopuksi näet esimerkin, joka osoittaa Java-verkostoitumisen Java 7: stä eteenpäin toteutettuna NIO.2: ssa.

Socket-ohjelmointi tarkoittaa kahta järjestelmää, jotka ovat yhteydessä toisiinsa. Yleensä verkkoviestinnässä on kaksi makua: Transport Control Protocol (TCP) ja User Datagram Protocol (UDP). TCP: tä ja UDP: tä käytetään eri tarkoituksiin, ja molemmilla on ainutlaatuiset rajoitukset:

  • TCP on suhteellisen yksinkertainen ja luotettava protokolla, jonka avulla asiakas voi muodostaa yhteyden palvelimeen ja molemmat järjestelmät kommunikoida. TCP: ssä kukin yksikkö tietää, että sen viestinnän hyötykuormat on vastaanotettu.
  • UDP on a Yhteydetön protokolla ja on hyvä tilanteisiin, joissa et välttämättä tarvitse kaikkia paketteja saapuaksesi määränpäähänsä, kuten median suoratoisto.

Arvioidaksesi eron TCP: n ja UDP: n välillä, mieti, mitä tapahtuisi, jos suoratoistat videota suosikkisivustoltasi ja se pudottaisi kehyksiä. Haluatko mieluummin, että asiakas hidastaa elokuvasi vastaanottamaan puuttuvat kehykset, vai haluaisitko, että videon toisto jatkuu? Videon suoratoistoprotokollat ​​käyttävät yleensä UDP: tä. Koska TCP takaa toimituksen, se on valittu protokolla HTTP: lle, FTP: lle, SMTP: lle, POP3: lle ja niin edelleen.

Tässä opetusohjelmassa esitän sinut Java-ohjelmointiin. Esitän sarjan asiakas-palvelin-esimerkkejä, jotka esittävät ominaisuuksia alkuperäisestä Java I / O -kehyksestä ja etenevät sitten vähitellen NIO: n sisältämien ominaisuuksien käyttöön.2.

Vanhan koulun Java-pistorasiat

NIO: ta edeltävissä toteutuksissa Java TCP -asiakasohjelmakoodikoodia käsittelee java.net. pistorasia luokassa. Seuraava koodi avaa yhteyden palvelimeen:

 Socket socket = uusi Socket (palvelin, portti); 

Kerran meidän pistorasiaan Esimerkki on kytketty palvelimelle, voimme alkaa hankkia tulo- ja lähtövirtoja erillislaitteeseen. Tulovirtauksia käytetään tietojen lukemiseen palvelimelta, kun taas lähtövirtauksia käytetään tietojen kirjoittamiseen palvelimelle. Voimme suorittaa seuraavat menetelmät tulo- ja lähtövirtojen saamiseksi:

 InputStream sisään = socket.getInputStream (); OutputStream out = socket.getOutputStream (); 

Koska nämä ovat tavallisia virtoja, samoja virtoja, joita käytettäisimme lukemaan tiedostosta ja kirjoittamaan tiedostoon, voimme muuntaa ne muotoon, joka parhaiten palvelee käyttötapaustamme. Voisimme esimerkiksi kääriä OutputStream kanssa PrintStream jotta voimme kirjoittaa tekstiä helposti esimerkiksi println (). Toisen esimerkin voisimme kääriä InputStream kanssa Puskuroitu lukija, kautta InputStreamReader, jotta tekstiä voidaan helposti lukea esimerkiksi Lue rivi().

lataa Lähdekoodi "Socket-ohjelmointi Java: opetusohjelma". Luonut Steven Haines JavaWorldille.

Esimerkki Java socket -asiakasohjelmasta

Selvitetään lyhyt esimerkki, joka suorittaa HTTP GET: n HTTP-palvelinta vastaan. HTTP on kehittyneempi kuin esimerkkimme sallii, mutta voimme kirjoittaa asiakaskoodin yksinkertaisen tapauksen käsittelemiseksi: pyydä resurssi palvelimelta ja palvelin palauttaa vastauksen ja sulkee virran. Tämä tapaus vaatii seuraavat vaiheet:

  1. Luo liitäntä verkkopalvelimelle, joka kuuntelee porttia 80.
  2. Hanki a PrintStream palvelimelle ja lähetä pyyntö HANKI PATH HTTP / 1.0, missä PATH on palvelimen pyydetty resurssi. Esimerkiksi, jos haluaisimme avata verkkosivuston juuren, polku olisi /.
  3. Hanki InputStream palvelimelle, kääri se a Puskuroitu lukija ja lukea vastaus rivi riviltä.

Listaus 1 näyttää tämän esimerkin lähdekoodin.

Listaus 1. SimpleSocketClientExample.java

paketti com.geekcap.javaworld.simplesocketclient; tuo java.io.BufferedReader; tuo java.io.InputStreamReader; tuo java.io.PrintStream; tuo java.net.Socket; public class SimpleSocketClientExample {public static void main (String [] args) {if (args.pituus <2) {System.out.println ("Käyttö: SimpleSocketClientExample"); System.exit (0); } Merkkijonopalvelin = args [0]; Merkkijonopolku = args [1]; System.out.println ("Ladataan URL-osoitteen sisältöä:" + palvelin); kokeile {// Yhdistä palvelimeen Socket socket = new Socket (server, 80); // Luo sisään- ja ulostulovirrat, joita voi lukea ja kirjoittaa palvelimelta. PrintStream out = new PrintStream (socket.getOutputStream ()); BufferedReader in = new BufferedReader (uusi InputStreamReader (socket.getInputStream ())); // Seuraa GET HTTP / 1.0: n HTTP-protokollaa, jota seuraa tyhjä rivi out.println ("GET" + polku + "HTTP / 1.0"); out.println (); // Lue tiedot palvelimelta, kunnes olemme lukeneet asiakirjan String line = in.readLine (); while (rivi! = null) {System.out.println (rivi); rivi = in.readLine (); } // Sulje virtamme in.close (); out.close (); pistorasia.sulje (); } catch (Poikkeus e) {e.printStackTrace (); }}} 

Listaus 1 hyväksyy kaksi komentoriviargumenttia: palvelin, johon yhteys muodostetaan (olettaen, että muodostamme yhteyden palvelimeen portissa 80) ja noutettava resurssi. Se luo Pistoke joka osoittaa palvelimeen ja määrittää nimenomaisesti portin 80. Sitten se suorittaa komennon:

HANKI PATH HTTP / 1.0 

Esimerkiksi:

GET / HTTP / 1.0 

Mitä juuri tapahtui?

Kun haet verkkosivun verkkopalvelimelta, kuten www.google.fi, HTTP-asiakas etsii palvelimen osoitteen DNS-palvelimien avulla: se aloitetaan pyytämällä ylätason verkkotunnuspalvelimelta com verkkotunnus, jossa arvovaltainen verkkotunnuspalvelin on www.google.fi. Sitten se pyytää kyseiseltä verkkotunnuspalvelimelta IP-osoitetta (tai osoitteita) www.google.fi. Seuraavaksi se avaa kannan tälle palvelimelle portissa 80. (Tai, jos haluat määrittää toisen portin, voit tehdä sen lisäämällä kaksoispisteen, jota seuraa portin numero, esimerkiksi: :8080.) Lopuksi HTTP-asiakas suorittaa määritetyn HTTP-menetelmän, kuten SAADA, LÄHETTÄÄ, LAITTAA, POISTAA, PÄÄtai OPTI / ONS. Jokaisella menetelmällä on oma syntaksinsa. Kuten yllä olevista koodinpätkistä näkyy, SAADA menetelmä edellyttää polkua, jota seuraa HTTP / versionumero ja tyhjä rivi. Jos halusimme lisätä HTTP-otsikot, olisimme voineet tehdä niin ennen uuden rivin syöttämistä.

Luettelossa 1 haimme OutputStream ja kääritty se a PrintStream jotta voimme helpommin suorittaa tekstipohjaiset komentomme. Koodimme sai InputStream, kääritty se InputStreamReader, joka muunsi sen a: ksi Lukijaja kääri sen sitten a Puskuroitu lukija. Käytimme PrintStream toteuttaa meidän SAADA menetelmää ja käytti sitten Puskuroitu lukija lukea vastauksia rivi kerrallaan, kunnes saimme a tyhjä vastaus, mikä osoittaa, että pistoke oli suljettu.

Suorita nyt tämä luokka ja anna sille seuraavat argumentit:

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

Sinun pitäisi nähdä samanlainen lähtö kuin alla:

Ladataan URL-osoitteen sisältöä: www.javaworld.com HTTP / 1.1 200 OK Päivämäärä: su, 21. syyskuuta 2014 22:20:13 GMT-palvelin: Apache X-Gas_TTL: 10 Välimuistin hallinta: max-ikä = 10 X-GasHost: gas2 .usw X-Cooking-With: Bensiini-Paikallinen X-Bensiini-Ikä: 8 Sisällön pituus: 168 Muokattu viimeksi: Tiistaina, 24. tammikuuta 2012 00:09:09 GMT Etag: "60001b-a8-4b73af4bf3340" Sisältötyyppi : teksti / html Vaihtelee: Hyväksy-koodaava yhteys: sulje bensiinitestisivu

Menestys

Tämä tulos näyttää testisivun JavaWorldin verkkosivustolla. Se vastasi, että puhuu HTTP-versiota 1.1 ja vastaus on 200 OK.

Esimerkki Java-socket-palvelimesta

Olemme käsittäneet asiakaspuolen ja onneksi palvelinpuolen viestintä on yhtä helppoa. Yksinkertaistetusta näkökulmasta prosessi on seuraava:

  1. Luo ServerSocket, määritetään portti kuunneltavaksi.
  2. Kutsu ServerSocketon hyväksyä() tapa kuunnella määritettyä porttia asiakasyhteydelle.
  3. Kun asiakas muodostaa yhteyden palvelimeen, hyväksyä() method palauttaa a Pistoke jonka kautta palvelin voi kommunikoida asiakkaan kanssa. Tämä on sama Pistoke luokka, jota käytimme asiakkaallemme, joten prosessi on sama: hanki InputStream lukea asiakkaalta ja OutputStream kirjoita asiakkaalle.
  4. Jos palvelimesi on oltava skaalautuva, sinun on siirrettävä Pistoke toiseen ketjuun käsiteltäväksi, jotta palvelimesi voi jatkaa lisäyhteyksien kuuntelua.
  5. Soita ServerSocketon hyväksyä() menetelmää uudelleen kuunnellaksesi toista yhteyttä.

Kuten pian näette, NIO: n käsittely tässä tilanteessa olisi hieman erilainen. Toistaiseksi voimme kuitenkin luoda suoraan ServerSocket välittämällä sille portti kuunneltavaksi (lisätietoja ServerSocketFactorys seuraavassa osassa):

 ServerSocket serverSocket = uusi ServerSocket (portti); 

Ja nyt voimme hyväksyä saapuvat yhteydet hyväksyä() menetelmä:

 Socket socket = serverSocket.accept (); // Käsittele yhteyttä ... 

Monisäikeinen ohjelmointi Java-liittimillä

Alla olevassa luettelossa 2 kaikki palvelinkoodi toistaiseksi yhdistetään hieman vankemmaksi esimerkiksi, joka käyttää ketjuja useiden pyyntöjen käsittelemiseen. Näytetty palvelin on kaikupalvelin, mikä tarkoittaa, että se toistaa kaikki saamansa viestit.

Vaikka Listing 2: n esimerkki ei ole monimutkainen, se ennakoi joitain NIO: n seuraavassa osassa esiintyviä asioita. Kiinnitä erityistä huomiota ketjutuskoodin määrään, joka meidän on kirjoitettava, jotta voimme rakentaa palvelimen, joka pystyy käsittelemään useita samanaikaisia ​​pyyntöjä.

Listaus 2. SimpleSocketServer.java

paketti com.geekcap.javaworld.simplesocketclient; tuo java.io.BufferedReader; tuoda java.io.I / OException; tuo java.io.InputStreamReader; tuo java.io.PrintWriter; tuo java.net.ServerSocket; tuo java.net.Socket; public class SimpleSocketServer laajentaa säiettä {private ServerSocket serverSocket; yksityinen int-portti; yksityinen totuusarvo = epätosi; public SimpleSocketServer (int-portti) {this.port = portti; } public void startServer () {kokeile {serverSocket = uusi ServerSocket (portti); tämä.aloitus (); } catch (I / OException e) {e.printStackTrace (); }} public void stopServer () {running = false; tämä. keskeytä (); } @Override public void run () {running = true; kun (käynnissä) {kokeile {System.out.println ("Yhteyden kuuntelu"); // Soita accept () seuraavan yhteyden vastaanottamiseksi Socket socket = serverSocket.accept (); // Siirrä liitäntä RequestHandler-säikeelle käsittelyä varten RequestHandler requestHandler = uusi RequestHandler (socket); requestHandler.start (); } catch (I / OException e) {e.printStackTrace (); }}} public static void main (Merkkijono [] args) {if (args.length == 0) {System.out.println ("Käyttö: SimpleSocketServer"); System.exit (0); } int port = Kokonaisluku.parseInt (argumentit [0]); System.out.println ("Käynnistä palvelin portissa:" + portti); SimpleSocketServer-palvelin = uusi SimpleSocketServer (portti); server.startServer (); // Sammutetaan automaattisesti minuutin kuluttua, kokeile {Thread.sleep (60000); } catch (Poikkeus e) {e.printStackTrace (); } server.stopServer (); }} luokka RequestHandler laajentaa Thread {private Socket socket; RequestHandler (Socket socket) {this.pistoke = pistorasia; } @Override public void run () {kokeile {System.out.println ("Vastaanotettu yhteys"); // Hae tulo- ja lähtövirrat BufferedReader in = new BufferedReader (new InputStreamReader (socket.getInputStream ())); PrintWriter out = uusi PrintWriter (socket.getOutputStream ()); // Kirjoita otsikko asiakkaalle out.println ("Echo Server 1.0"); ulos. huuhtele (); // Kaiku linjat takaisin asiakkaalle, kunnes asiakas sulkee yhteyden tai saamme tyhjän rivin String line = in.readLine (); while (viiva! = null && viiva.pituus ()> 0) {out.println ("Kaiku:" + viiva); ulos. huuhtele (); rivi = in.readLine (); } // Sulje yhteys in.close (); out.close (); pistorasia.sulje (); System.out.println ("Yhteys suljettu"); } catch (Poikkeus e) {e.printStackTrace (); }}}