java.io
balíček a NIO, neblokujúci I / O (java.nio
) API zavedené v prostredí Java 1.4. Na záver uvidíte príklad, ktorý demonštruje vytváranie sietí Java v prostredí NIO.2 implementovanom od Java 7 vpred.Programovanie zásuviek sa zredukuje na dva systémy, ktoré navzájom komunikujú. Sieťová komunikácia sa všeobecne dodáva v dvoch variantoch: Transport Control Protocol (TCP) a User Datagram Protocol (UDP). TCP a UDP sa používajú na rôzne účely a obe majú jedinečné obmedzenia:
- TCP je relatívne jednoduchý a spoľahlivý protokol, ktorý umožňuje klientovi nadviazať spojenie so serverom a dvoma systémami na komunikáciu. V TCP každá entita vie, že boli prijaté jej užitočné zaťaženia komunikácie.
- UDP je a protokol bez pripojenia a je vhodný pre scenáre, keď nevyhnutne nepotrebujete každý paket, aby ste dorazili na miesto určenia, napríklad streamovanie médií.
Ak chcete oceniť rozdiel medzi TCP a UDP, zvážte, čo by sa stalo, keby ste streamovali video z vášho obľúbeného webu a vypadli z neho rámce. Chceli by ste, aby klient spomalil váš film, aby získal chýbajúce snímky, alebo chcete, aby sa video prehrávalo ďalej? Protokoly na streamovanie videa zvyčajne využívajú UDP. Pretože TCP zaručuje doručenie, je to protokol voľby pre HTTP, FTP, SMTP, POP3 atď.
V tomto návode vám predstavím programovanie zásuviek v Jave. Uvádzam sériu príkladov typu klient-server, ktoré demonštrujú funkcie z pôvodného prostredia Java I / O a postupne postupujú k používaniu funkcií predstavených v aplikácii NIO.2.
Staré školské zásuvky Java
V implementáciách pred NIO kód klientskej zásuvky Java TCP spracúva server java.net.Socket
trieda. Nasledujúci kód otvára pripojenie k serveru:
Zásuvka zásuvky = nová zásuvka (server, port);
Raz náš zásuvka
inštancia je pripojená k serveru, môžeme začať získavať vstupné a výstupné toky do servera. Vstupné toky sa používajú na čítanie údajov zo servera, zatiaľ čo výstupné toky sa používajú na zápis údajov na server. Na získanie vstupných a výstupných tokov môžeme vykonať nasledujúce metódy:
InputStream in = socket.getInputStream (); Výstup OutStream = socket.getOutputStream ();
Pretože sa jedná o bežné streamy, rovnaké streamy, ktoré by sme použili na čítanie a zápis do súboru, môžeme ich previesť do formy, ktorá najlepšie slúži nášmu prípadu použitia. Napríklad by sme mohli zabaliť OutputStream
s PrintStream
aby sme mohli ľahko písať text metódami ako println ()
. Ako ďalší príklad by sme mohli zabaliť InputStream
s BufferedReader
prostredníctvom InputStreamReader
, aby bolo možné ľahko čítať text metódami ako readLine ()
.
Príklad klienta soketu Java
Prejdime si krátky príklad, ktorý vykoná HTTP GET proti serveru HTTP. Protokol HTTP je sofistikovanejší, ako povoľuje náš príklad, ale môžeme napísať kód klienta, aby sme zvládli najjednoduchší prípad: vyžiadať si zo servera zdroj a server vráti odpoveď a zavrie stream. Tento prípad vyžaduje nasledujúce kroky:
- Vytvorte zásuvku pre webový server počúvajúci na porte 80.
- Získajte a
PrintStream
odoslať na serverZÍSKAJTE CESTU HTTP / 1.0
, kdeCESTA
je požadovaný prostriedok na serveri. Napríklad, ak by sme chceli otvoriť koreň webovej stránky, potom by bola cesta/
. - Získať
InputStream
na server, zabaľte ho doBufferedReader
a prečítajte si odpoveď po riadkoch.
Výpis 1 zobrazuje zdrojový kód tohto príkladu.
Zoznam 1. SimpleSocketClientExample.java
balíček com.geekcap.javaworld.simplesocketclient; import java.io.BufferedReader; import java.io.InputStreamReader; import java.io.PrintStream; import java.net.Socket; verejná trieda SimpleSocketClientExample {verejné statické void main (String [] args) {if (args.length <2) {System.out.println ("Použitie: SimpleSocketClientExample"); System.exit (0); } Reťazcový server = args [0]; Cesta reťazca = args [1]; System.out.println ("Načítava sa obsah adresy URL:" + server); skúste {// Pripojiť k serveru Socket socket = new Socket (server, 80); // Vytváranie vstupných a výstupných prúdov na čítanie a zápis na server PrintStream out = nový PrintStream (socket.getOutputStream ()); BufferedReader in = nový BufferedReader (nový InputStreamReader (socket.getInputStream ())); // Postupujte podľa protokolu HTTP GET HTTP / 1.0, za ktorým nasleduje prázdny riadok out.println ("GET" + cesta + "HTTP / 1.0"); out.println (); // Čítame údaje zo servera, kým nedočítame dokument String line = in.readLine (); while (riadok! = null) {System.out.println (riadok); line = in.readLine (); } // Zatvorte naše streamy in.close (); out.close (); socket.close (); } catch (Výnimka e) {e.printStackTrace (); }}}
Zoznam 1 prijíma dva argumenty príkazového riadku: server, ku ktorému sa chcete pripojiť (za predpokladu, že sa pripájame k serveru na porte 80), a prostriedok, ktorý sa má načítať. Vytvára a Zásuvka
ktorý smeruje na server a výslovne určuje port 80
. Potom vykoná príkaz:
ZÍSKAJTE CESTU HTTP / 1.0
Napríklad:
GET / HTTP / 1.0
Čo sa práve stalo?
Pri načítaní webovej stránky z webového servera, ako je napr www.google.com
, HTTP klient používa servery DNS na zistenie adresy servera: začína sa tým, že sa od servera domény najvyššej úrovne požaduje server com
doména, kde je smerodajný server názvov domén pre doménu www.google.com
. Potom požiada server doménových mien o adresu IP (alebo adresy) www.google.com
. Ďalej otvorí soket na tomto serveri na porte 80. (Alebo ak chcete definovať iný port, môžete to urobiť pridaním dvojbodky a čísla portu, napríklad: :8080
.) Nakoniec klient HTTP vykoná zadanú metódu HTTP, napríklad ZÍSKAJTE
, POST
, PUT
, ODSTRÁNIŤ
, HLAVA
alebo MOŽNOSTI
. Každá metóda má svoju vlastnú syntax. Ako je uvedené vo vyššie uvedenom výpise kódu, znak ZÍSKAJTE
metóda vyžaduje cestu, za ktorou nasleduje HTTP / číslo verzie
a prázdny riadok. Ak by sme chceli pridať hlavičky HTTP, mohli sme to urobiť pred zadaním nového riadku.
V zozname 1 sme získali OutputStream
a zabalil do a PrintStream
aby sme mohli ľahšie vykonávať naše textové príkazy. Náš kód získal InputStream
, zabalené v InputStreamReader
, ktorá ho previedla na a Čitateľ
, a potom to zabalil do a BufferedReader
. Použili sme PrintStream
vykonať našu ZÍSKAJTE
metóda a potom sa použila metóda BufferedReader
čítať odpoveď riadok po riadku, kým nedostaneme a nulový
odpoveď, ktorá naznačuje, že soket bol zatvorený.
Teraz vykonajte túto triedu a odovzdajte jej nasledujúce argumenty:
java com.geekcap.javaworld.simplesocketclient.SimpleSocketClientExample www.javaworld.com /
Mali by ste vidieť výstup podobný nasledujúcemu:
Načítava sa obsah adresy URL: www.javaworld.com HTTP / 1,1 200 OK Dátum: ne, 21. septembra 2014 22:20:13 GMT server: Apache X-Gas_TTL: 10 Cache-Control: max-age = 10 X-GasHost: gas2 .usw X-Cooking-With: benzín-lokálny X-benzín-vek: 8 obsahová dĺžka: 168 posledná zmena: ut, 24. januára 2012 00:09:09 GMT Etag: „60001b-a8-4b73af4bf3340“ obsahový typ : text / html Variabilné: Pripojenie na prijímanie a kódovanie: zatvorte stránku na testovanie benzínuÚspech
Tento výstup zobrazuje testovaciu stránku na webe JavaWorld. Odpovedal späť, že hovorí HTTP verzia 1.1, a odpoveď je 200 OK
.
Príklad servera Java socket
Pokryli sme stranu klienta a našťastie je komunikačný aspekt na strane servera rovnako ľahký. Zo zjednodušeného hľadiska je proces nasledovný:
- Vytvor
ServerSocket
, s uvedením portu, na ktorom sa má počúvať. - Vyvolajte
ServerSocket
jesúhlasiť()
metóda počúvania na nakonfigurovanom porte pre pripojenie klienta. - Keď sa klient pripojí k serveru, server
súhlasiť()
metóda vracia aZásuvka
prostredníctvom ktorého môže server komunikovať s klientom. To je to istéZásuvka
triedy, ktorú sme použili pre nášho klienta, takže postup je rovnaký: získajteInputStream
čítať od klienta aOutputStream
napíš klientovi. - Ak je potrebné, aby bol váš server škálovateľný, mali by ste vyhovieť
Zásuvka
do iného vlákna na spracovanie, aby váš server mohol pokračovať v počúvaní ďalších pripojení. - Zavolajte
ServerSocket
jesúhlasiť()
znova počúvať ďalšie pripojenie.
Ako čoskoro uvidíte, zaobchádzanie NIO s týmto scenárom by bolo trochu iné. Zatiaľ však môžeme priamo vytvoriť a ServerSocket
odovzdaním portu, ktorý chcete počúvať (viac o ServerSocketFactory
s v ďalšej časti):
ServerSocket serverSocket = nový ServerSocket (port);
A teraz môžeme prijímať prichádzajúce spojenia cez súhlasiť()
metóda:
Socket socket = serverSocket.accept (); // Riešenie spojenia ...
Viacvláknové programovanie so zásuvkami Java
Výpis 2 nižšie dáva všetok doterajší kód servera dohromady do trochu robustnejšieho príkladu, ktorý používa vlákna na vybavenie viacerých požiadaviek. Zobrazený server je echo server, čo znamená, že odráža všetky správy, ktoré prijme.
Aj keď príklad v zozname 2 nie je zložitý, predpokladá niečo z toho, čo sa chystá v nasledujúcej časti o NIO. Venujte zvláštnu pozornosť množstvu vláknového kódu, ktorý musíme napísať, aby sme mohli vytvoriť server, ktorý zvládne viac súčasných požiadaviek.
Zoznam 2. SimpleSocketServer.java
balíček com.geekcap.javaworld.simplesocketclient; import java.io.BufferedReader; import java.io.I / OException; import java.io.InputStreamReader; import java.io.PrintWriter; import java.net.ServerSocket; import java.net.Socket; verejná trieda SimpleSocketServer rozširuje vlákno {private ServerSocket serverSocket; súkromný int port; private boolean running = false; public SimpleSocketServer (int port) {this.port = port; } public void startServer () {try {serverSocket = nový ServerSocket (port); this.start (); } catch (I / OException e) {e.printStackTrace (); }} public void stopServer () {running = false; this.interrupt (); } @Override public void run () {running = true; while (running) {try {System.out.println ("Listening for a connection"); // Zavolajte accept () na prijatie ďalšieho spojenia Socket socket = serverSocket.accept (); // Preneste soket na vlákno RequestHandler na spracovanie RequestHandler requestHandler = nový RequestHandler (socket); requestHandler.start (); } catch (I / OException e) {e.printStackTrace (); }}} public static void main (String [] args) {if (args.length == 0) {System.out.println ("Použitie: SimpleSocketServer"); System.exit (0); } int port = Integer.parseInt (args [0]); System.out.println ("Spustiť server na porte:" + port); Server SimpleSocketServer = nový SimpleSocketServer (port); server.startServer (); // Automatické vypnutie za 1 minútu skúste {Thread.sleep (60000); } catch (Výnimka e) {e.printStackTrace (); } server.stopServer (); }} class RequestHandler extends Thread {private Socket socket; RequestHandler (Socket socket) {this.socket = socket; } @Override public void run () {try {System.out.println ("Prijalo pripojenie"); // Získanie vstupných a výstupných prúdov BufferedReader in = new BufferedReader (nový InputStreamReader (socket.getInputStream ())); PrintWriter out = nový PrintWriter (socket.getOutputStream ()); // Vypíšeme našu hlavičku klientovi out.println ("Echo Server 1.0"); out.flush (); // Odozva riadkov späť na klienta, kým klient neukončí pripojenie alebo kým nedostaneme prázdny riadok String line = in.readLine (); while (line! = null && line.length ()> 0) {out.println ("Echo:" + riadok); out.flush (); line = in.readLine (); } // Uzavrieme naše spojenie in.close (); out.close (); socket.close (); System.out.println ("Pripojenie ukončené"); } catch (Výnimka e) {e.printStackTrace (); }}}