Programavimas

„Socket“ programavimas „Java“: pamoka

Ši pamoka yra įvadas į „Java“ lizdų programavimą, pradedant paprastu kliento-serverio pavyzdžiu, parodančiu pagrindines „Java I / O“ savybes. Susipažinsite su originalujava.io paketas ir NIO, neužblokuojantis I / O (java.nio) API, įdiegtos „Java 1.4“. Galiausiai pamatysite pavyzdį, kuris parodo „Java“ tinklą, įdiegtą „Java 7“ į priekį, NIO.2.

Lizdo programavimas yra susijęs su dviem sistemomis, kurios bendrauja tarpusavyje. Paprastai tinklo ryšys būna dviejų skonių: Transport Control Protocol (TCP) ir User Datagram Protocol (UDP). TCP ir UDP naudojami skirtingiems tikslams ir jiems abiem būdingi unikalūs apribojimai:

  • TCP yra palyginti paprastas ir patikimas protokolas, leidžiantis klientui užmegzti ryšį su serveriu ir abiem sistemomis bendrauti. TCP kiekvienas subjektas žino, kad buvo gautos jo komunikacijos naudingosios apkrovos.
  • UDP yra a bevielis protokolas ir tinka scenarijams, kai nebūtinai reikia kiekvieno paketo, kad pasiektumėte tikslą, pvz., medijos srautui.

Norėdami įvertinti skirtumą tarp TCP ir UDP, apsvarstykite, kas nutiktų, jei transliuotumėte vaizdo įrašą iš savo mėgstamos svetainės ir jis nukristų nuo rėmelių. Ar norėtumėte, kad klientas sulėtintų jūsų filmą, kad gautų trūkstamus kadrus, ar norėtumėte, kad vaizdo įrašas būtų rodomas toliau? Vaizdo perdavimo protokolai paprastai naudoja UDP. Kadangi TCP garantuoja pristatymą, tai yra HTTP, FTP, SMTP, POP3 ir kt. Pasirinktas protokolas.

Šioje pamokoje aš supažindinu jus su „Java“ lizdų programavimu. Pateikiu seriją kliento-serverio pavyzdžių, kurie demonstruoja originalios „Java I / O“ sistemos ypatybes, tada palaipsniui pereina prie NIO įdiegtų funkcijų.2.

Senosios mokyklos „Java“ lizdai

Diegiant iki NIO, „Java TCP“ kliento lizdo kodą tvarko java.net.Socket klasė. Šis kodas atveria ryšį su serveriu:

 Lizdo lizdas = naujas Lizdas (serveris, prievadas); 

Kartą mūsų lizdas egzempliorius yra prijungtas prie serverio, mes galime pradėti gauti įvesties ir išvesties srautus į atskirą. Įvesties srautai naudojami duomenims iš serverio nuskaityti, o išvesties srautai naudojami duomenims rašyti į serverį. Norėdami gauti įvesties ir išvesties srautus, galime atlikti šiuos metodus:

 „InputStream“ = socket.getInputStream (); OutputStream out = socket.getOutputStream (); 

Kadangi tai yra įprasti srautai, tie patys srautai, kuriuos naudotume skaitydami ir rašydami į failą, galime juos konvertuoti į formą, kuri geriausiai tinka mūsų naudojimo atvejui. Pavyzdžiui, mes galėtume apvynioti „OutputStream“ su „PrintStream“ kad galėtume lengvai rašyti tekstą tokiais metodais kaip println (). Kitą pavyzdį galėtume apvynioti „InputStream“ su „BufferedReader“, per an „InputStreamReader“, norint lengvai perskaityti tekstą tokiais metodais kaip readLine ().

atsisiųsti Atsisiųskite šaltinio kodą „Socket programavimas„ Java “: pamoka“. Stevenas Hainesas sukūrė „JavaWorld“.

„Java socket“ kliento pavyzdys

Panagrinėkime trumpą pavyzdį, kuris vykdo HTTP GET prieš HTTP serverį. HTTP yra sudėtingesnis nei leidžia mūsų pavyzdys, tačiau mes galime parašyti kliento kodą paprasčiausiam atvejui: paprašykite iš serverio išteklių, o serveris grąžina atsakymą ir uždaro srautą. Šiuo atveju reikia atlikti šiuos veiksmus:

  1. Sukurkite tinklo serverio, klausančio 80 prievado, lizdą.
  2. Gaukite a „PrintStream“ į serverį ir išsiųskite užklausą GAUTI KELIĄ HTTP / 1.0, kur KELIS yra prašomas šaltinis serveryje. Pavyzdžiui, jei norėtume atidaryti svetainės šaknį, kelias būtų /.
  3. Gaukite „InputStream“ į serverį, apvyniokite jį a „BufferedReader“ ir skaityti atsakymą eilutėmis.

1 sąraše rodomas šio pavyzdžio šaltinio kodas.

Sąrašas 1. SimpleSocketClientExample.java

paketas com.geekcap.javaworld.simplesocketclient; importuoti java.io.BufferedReader; importuoti java.io.InputStreamReader; importuoti java.io.PrintStream; importuoti java.net.Socket; public class SimpleSocketClientExample {public static void main (String [] args) {if (args.length <2) {System.out.println ("Naudojimas: SimpleSocketClientExample"); System.exit (0); } Stygų serveris = args [0]; Stygos kelias = args [1]; System.out.println ("Įkeliamas URL turinys:" + serveris); pabandykite {// Prisijunkite prie serverio „Socket socket“ = naujas „Socket“ (serveris, 80); // Sukurkite įvesties ir išvesties srautus, kad galėtumėte skaityti iš serverio ir rašyti į jį. PrintStream out = new PrintStream (socket.getOutputStream ()); BufferedReader in = new BufferedReader (nauja InputStreamReader (socket.getInputStream ())); // Vykdykite GET HTTP / 1.0 HTTP protokolą, po kurio tuščia eilutė out.println ("GET" + kelias + "HTTP / 1.0"); out.println (); // Skaityti duomenis iš serverio, kol baigsime skaityti dokumentą String line = in.readLine (); while (eilutė! = null) {System.out.println (eilutė); eilutė = in.readLine (); } // Uždarykite mūsų srautus in.close (); out.close (); lizdas.uždaryti (); } sugavimas (e išimtis) {e.printStackTrace (); }}} 

1 sąraše priimami du komandinės eilutės argumentai: serveris, prie kurio reikia prisijungti (darant prielaidą, kad jungiamės prie 80 prievado serverio) ir šaltinis, kurį reikia gauti. Tai sukuria Lizdas kuris nurodo serverį ir aiškiai nurodo prievadą 80. Tada jis vykdo komandą:

GAUTI KELIĄ HTTP / 1.0 

Pavyzdžiui:

GET / HTTP / 1.0 

Kas ką tik nutiko?

Kai gaunate tinklalapį iš žiniatinklio serverio, pvz www.google.lt, HTTP klientas naudoja DNS serverius ieškodamas serverio adreso: jis pirmiausia prašomas aukščiausiojo lygio domeno serverio com domenas, kuriame yra autoritetingas domenų vardų serveris www.google.lt. Tada jis paprašo to domenų vardų serverio nurodyti IP adresą (ar adresus) www.google.lt. Tada jis atidaro lizdą tam serveriui 80 prievade. (Arba, jei norite apibrėžti kitą prievadą, galite tai padaryti pridėdami dvitaškį, po kurio yra prievado numeris, pavyzdžiui: :8080.) Galiausiai, HTTP klientas vykdo nurodytą HTTP metodą, pvz GAUTI, POST, ĮDĖK, IŠTRINTI, GALVAarba GALIMYBĖS. Kiekvienas metodas turi savo sintaksę. Kaip parodyta pirmiau pateiktame kodo fragmente, GAUTI metodas reikalauja kelio, kuriuo eina HTTP / versijos numeris ir tuščia eilutė. Jei norėtume pridėti HTTP antraštes, tai galėjome padaryti prieš įvesdami naują eilutę.

1 sąraše gavome „OutputStream“ ir suvyniojo į a „PrintStream“ kad galėtume lengviau vykdyti savo teksto komandas. Mūsų kodas gavo „InputStream“, suvyniojo tai į „InputStreamReader“, kuris pavertė jį a Skaitytojasir tada suvyniojo tai į „BufferedReader“. Mes naudojome „PrintStream“ įvykdyti mūsų GAUTI metodą ir tada naudojo „BufferedReader“ skaityti atsakymą eilutėmis, kol gausime a niekinis atsakymas, rodantis, kad lizdas buvo uždarytas.

Dabar vykdykite šią klasę ir pateikite jai šiuos argumentus:

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

Turėtumėte pamatyti išvestį, panašią į žemiau pateiktą:

Įkeliamas URL turinys: www.javaworld.com HTTP / 1.1 200 OK Data: 2014 m. Rugsėjo 21 d., Sekmadienis, 22:20:13 GMT serveris: „Apache X-Gas_TTL“: 10 „Cache-Control“: maksimalus amžius = 10 „X-GasHost“: gas2 .usw X-Cooking-With: Benzinas-Vietinis X-Benzinas-Amžius: 8 Turinio ilgis: 168 Paskutinį kartą modifikuotas: 2012 m. sausio 24 d., antradienį, 00:09:09 GMT Etag: "60001b-a8-4b73af4bf3340" Turinio tipas : text / html Vary: Priimti kodavimo ryšį: uždaryti benzino bandymo puslapį

Sėkmė

Šiame išvestyje rodomas bandomasis puslapis „JavaWorld“ svetainėje. Jis atsakė, kad kalba HTTP 1.1 versija ir atsakymas yra 200 Gerai.

„Java“ lizdo serverio pavyzdys

Apžvelgėme kliento pusę ir, laimei, serverio pusės komunikacijos aspektas yra toks pat lengvas. Žvelgiant iš supaprastintos perspektyvos, procesas yra toks:

  1. Sukurti „ServerSocket“, nurodant uostą, kurio klausytis.
  2. Iškvieskite „ServerSocket“'s priimti() metodas klausytis sukonfigūruoto kliento ryšio prievado.
  3. Kai klientas prisijungia prie serverio, priimti() metodas grąžina a Lizdas per kurį serveris gali bendrauti su klientu. Tai tas pats Lizdas klasę, kurią naudojome savo klientui, taigi procesas yra tas pats: gaukite „InputStream“ skaityti iš kliento ir „OutputStream“ parašyti klientui.
  4. Jei jūsų serveris turi būti keičiamas, norėsite perduoti Lizdas į kitą temą, kurią reikia apdoroti, kad jūsų serveris galėtų toliau klausytis papildomų ryšių.
  5. Skambinkite „ServerSocket“'s priimti() metodas vėl klausytis kito ryšio.

Kaip netrukus pamatysite, NIO valdys šį scenarijų šiek tiek kitaip. Tačiau kol kas galime tiesiogiai sukurti „ServerSocket“ perduodamas uostą klausytis (daugiau apie „ServerSocketFactory“s kitame skyriuje):

 ServerSocket serverSocket = naujas ServerSocket (prievadas); 

Ir dabar mes galime priimti gaunamus ryšius per priimti() metodas:

 Lizdo lizdas = serverSocket.accept (); // Tvarkykite ryšį ... 

Daugiagrybis programavimas su „Java“ lizdais

Žemiau pateiktame 2 sąraše visas iki šiol pateiktas serverio kodas sujungiamas į šiek tiek patikimesnį pavyzdį, kuris naudoja gijas tvarkant kelias užklausas. Rodomas serveris yra aido serveris, tai reiškia, kad jis atkartoja bet kokią gautą žinutę.

Nors 2 sąrašo pavyzdys nėra sudėtingas, jis numato kai kuriuos dalykus kitame NIO skyriuje. Atkreipkite ypatingą dėmesį į sriegimo kodo kiekį, kurį turime parašyti, kad sukurtume serverį, galintį apdoroti kelias vienu metu pateiktas užklausas.

Sąrašas 2. SimpleSocketServer.java

paketas com.geekcap.javaworld.simplesocketclient; importuoti java.io.BufferedReader; importuoti java.io.I / OException; importuoti java.io.InputStreamReader; importuoti java.io.PrintWriter; importuoti java.net.ServerSocket; importuoti java.net.Socket; viešosios klasės „SimpleSocketServer“ išplečia „Thread“ {privatų „ServerSocket“ serverSocket; privatus int uostas; privatus loginis bėgimas = klaidingas; public SimpleSocketServer (int portas) {this.port = prievadas; } public void startServer () {try {serverSocket = naujas ServerSocket (prievadas); tai.pradėti (); } gaudyti (I / OException e) {e.printStackTrace (); }} public void stopServer () {running = false; tai.traukti (); } @Paisyti viešo negaliojančio bėgimo () {running = true; o (veikia) {pabandykite {System.out.println ("Ryšio klausymas"); // Skambinkite accept (), norėdami gauti kitą ryšį Socket socket = serverSocket.accept (); // Perduokite lizdą „RequestHandler“ gijai apdoroti „RequestHandler requestHandler“ = naujas „RequestHandler“ (lizdas); requestHandler.start (); } gaudyti (I / OException e) {e.printStackTrace (); }}} public static void main (String [] args) {if (args.length == 0) {System.out.println ("Naudojimas: SimpleSocketServer"); System.exit (0); } int port = Sveikasis skaičius.parseInt (argumentai [0]); System.out.println ("Pradėti serverį uoste:" + prievadas); „SimpleSocketServer“ serveris = naujas „SimpleSocketServer“ (prievadas); server.startServer (); // Automatiškai išsijungti per 1 minutę, pabandykite {Thread.sleep (60000); } sugavimas (e išimtis) {e.printStackTrace (); } server.stopServer (); }} klasės „RequestHandler“ išplečia „Thread“ {private Socket socket; „RequestHandler“ (lizdo lizdas) {this.socket = lizdas; } @Paisyti viešojo tuštumo vykdymą () {pabandykite {System.out.println ("Gavau ryšį"); // Gauti įvesties ir išvesties srautus „BufferedReader“ = naujas „BufferedReader“ (naujas „InputStreamReader“ („socket.getInputStream“))); „PrintWriter out“ = naujas „PrintWriter“ („socket.getOutputStream“); // Išrašykite klientui mūsų antraštę out.println ("Echo Server 1.0"); out.plauti (); // Echo eilutės atgal į klientą, kol klientas uždarys ryšį arba mes gausime tuščią eilutę String line = in.readLine (); while (eilutė! = null && line.length ()> 0) {out.println ("Echo:" + eilutė); out.plauti (); eilutė = in.readLine (); } // Uždarykite mūsų ryšį.close (); out.close (); lizdas.uždaryti (); System.out.println ("Ryšys uždarytas"); } sugavimas (e išimtis) {e.printStackTrace (); }}}