Programování

Programování soketů v Javě: Výukový program

Tento kurz je úvodem do programování soketů v Javě, počínaje jednoduchým příkladem klient-server, který demonstruje základní funkce Java I / O. Bude vám představen origináljava.io balíček a NIO, neblokující I / O (java.nio) Rozhraní API zavedená v prostředí Java 1.4. Nakonec uvidíte příklad, který demonstruje síťové prostředí Java implementované z prostředí Java 7 vpřed v NIO.2.

Programování soketů se redukuje na dva systémy, které spolu komunikují. Síťová komunikace má obecně dvě varianty: Transport Control Protocol (TCP) a User Datagram Protocol (UDP). TCP a UDP se používají pro různé účely a oba mají jedinečná omezení:

  • TCP je relativně jednoduchý a spolehlivý protokol, který umožňuje klientovi navázat spojení se serverem a dvěma systémy ke komunikaci. V TCP každá entita ví, že byla přijata její užitečná zatížení komunikace.
  • UDP je a protokol bez připojení a je vhodný pro scénáře, kdy k doručení na místo určení nutně nepotřebujete každý paket, například streamování médií.

Chcete-li ocenit rozdíl mezi TCP a UDP, zvažte, co by se stalo, kdybyste streamovali video ze svého oblíbeného webu a vypadly z něj snímky. Dáváte přednost tomu, aby klient zpomalil váš film, aby získal chybějící snímky, nebo chcete, aby video pokračovalo v přehrávání? Protokoly pro streamování videa obvykle využívají UDP. Protože TCP zaručuje doručení, je to protokol volby pro HTTP, FTP, SMTP, POP3 atd.

V tomto tutoriálu vám představím programování socketů v Javě. Představuji řadu příkladů typu klient-server, které demonstrují funkce z původního prostředí Java I / O, poté postupně postupuji k používání funkcí zavedených v NIO.2.

Staré školní zásuvky Java

V implementacích před NIO je kód soketu klienta Java TCP zpracováván java.net.Socket třída. Následující kód otevírá připojení k serveru:

 Socket socket = nový Socket (server, port); 

Jednou naše zásuvka instance je připojena k serveru, můžeme začít získávat vstupní a výstupní toky na sever. Vstupní toky se používají ke čtení dat ze serveru, zatímco výstupní toky se používají k zápisu dat na server. K získání vstupních a výstupních proudů můžeme provést následující metody:

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

Protože se jedná o běžné streamy, stejné streamy, které bychom použili ke čtení a zápisu do souboru, můžeme je převést do formy, která nejlépe vyhovuje našemu případu použití. Mohli bychom například zabalit Výstupní proud s PrintStream abychom mohli snadno psát text metodami jako println (). Jako další příklad bychom mohli zabalit InputStream s Vyrovnávací paměťprostřednictvím InputStreamReader, aby bylo možné snadno číst text pomocí metod jako readLine ().

stáhnout Stáhněte si zdrojový kód Zdrojový kód pro „Programování soketů v Javě: Výukový program“. Vytvořil Steven Haines pro JavaWorld.

Příklad klienta Java socket

Pojďme si projít krátký příklad, který provede HTTP GET proti serveru HTTP. HTTP je sofistikovanější, než umožňuje náš příklad, ale můžeme napsat klientský kód pro zpracování nejjednoduššího případu: požádat o zdroj ze serveru a server vrátí odpověď a zavře stream. Tento případ vyžaduje následující kroky:

  1. Vytvořte soket na webový server naslouchající na portu 80.
  2. Získejte a PrintStream na server a odeslat požadavek ZÍSKEJTE CESTU HTTP / 1.0, kde CESTA je požadovaný zdroj na serveru. Například pokud bychom chtěli otevřít kořen webové stránky, pak by cesta byla /.
  3. Získejte InputStream na server, zabalte jej do Vyrovnávací paměť a přečíst odpověď řádek po řádku.

Výpis 1 ukazuje zdrojový kód pro tento příklad.

Výpis 1. SimpleSocketClientExample.java

balíček com.geekcap.javaworld.simplesocketclient; importovat java.io.BufferedReader; importovat java.io.InputStreamReader; import java.io.PrintStream; import java.net.Socket; veřejná třída SimpleSocketClientExample {public static void main (String [] args) {if (args.length <2) {System.out.println ("Použití: SimpleSocketClientExample"); System.exit (0); } Řetězcový server = args [0]; Cesta řetězce = args [1]; System.out.println ("Načítání obsahu URL:" + server); zkuste {// Připojit k serveru Socket socket = new Socket (server, 80); // Vytváření vstupních a výstupních proudů pro čtení a zápis na server PrintStream out = nový PrintStream (socket.getOutputStream ()); BufferedReader in = new BufferedReader (new InputStreamReader (socket.getInputStream ())); // Postupujte podle protokolu HTTP GET HTTP / 1.0 následovaného prázdným řádkem out.println ("GET" + cesta + "HTTP / 1.0"); out.println (); // Číst data ze serveru, dokud nedokončíme čtení dokumentu String line = in.readLine (); while (řádek! = null) {System.out.println (řádek); line = in.readLine (); } // Zavřete naše streamy in.close (); out.close (); socket.close (); } catch (Výjimka e) {e.printStackTrace (); }}} 

Výpis 1 přijímá dva argumenty příkazového řádku: server, ke kterému se chcete připojit (za předpokladu, že se připojujeme k serveru na portu 80), a prostředek, který se má načíst. Vytváří Zásuvka který ukazuje na server a výslovně určuje port 80. Poté provede příkaz:

ZÍSKEJTE CESTU HTTP / 1.0 

Například:

GET / HTTP / 1.0 

Co se právě stalo?

Když načítáte webovou stránku z webového serveru, například www.google.com„Klient HTTP používá servery DNS k vyhledání adresy serveru: začíná tím, že požádá server domény nejvyšší úrovně o adresu com doména, kde je autoritativní server doménových jmen pro www.google.com. Poté požádá server doménových jmen o adresu IP (nebo adresy) www.google.com. Dále otevře soket na tomto serveru na portu 80. (Nebo pokud chcete definovat jiný port, můžete to udělat přidáním dvojtečky následované číslem portu, například: :8080.) Nakonec klient HTTP provede zadanou metodu HTTP, například DOSTAT, POŠTA, DÁT, VYMAZAT, HLAVAnebo OPTI / ONS. Každá metoda má svou vlastní syntaxi. Jak je znázorněno ve výše uvedeném kódu, DOSTAT metoda vyžaduje cestu následovanou Číslo HTTP / verze a prázdný řádek. Pokud bychom chtěli přidat záhlaví HTTP, mohli bychom to udělat před zadáním nového řádku.

V seznamu 1 jsme načetli Výstupní proud a zabalil to do PrintStream abychom mohli snadněji vykonávat naše textové příkazy. Náš kód získal InputStreamzabalil to do InputStreamReader, který jej převedl na a Čtenář, a pak to zabalil do a Vyrovnávací paměť. Použili jsme PrintStream vykonat naši DOSTAT metoda a poté použila Vyrovnávací paměť číst odpověď řádek po řádku, dokud nedostaneme a nula odpověď, což naznačuje, že zásuvka byla uzavřena.

Nyní spusťte tuto třídu a předejte jí následující argumenty:

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

Měli byste vidět výstup podobný tomu, co je níže:

Načítání obsahu URL: www.javaworld.com HTTP / 1.1 200 OK Datum: Ne, 21. září 2014 22:20:13 GMT Server: Apache X-Gas_TTL: 10 Cache-Control: max-age = 10 X-GasHost: gas2 .usw X-vaření s: benzín-místní X-benzín-věk: 8 délka obsahu: 168 poslední změna: út, 24. ledna 2012 00:09:09 GMT štítek: "60001b-a8-4b73af4bf3340" obsahový typ : text / html Různé: Přijmout a kódovat připojení: zavřít stránku s testováním benzínu

Úspěch

Tento výstup ukazuje testovací stránku na webu JavaWorld. Odpovědělo zpět, že mluví HTTP verze 1.1 a odpověď je 200 OK.

Příklad serveru Java socket

Pokryli jsme klientskou stranu a naštěstí je komunikační aspekt na straně serveru stejně snadný. Ze zjednodušené perspektivy je proces následující:

  1. Vytvořit ServerSocket, určující port, na kterém se má poslouchat.
  2. Vyvolejte ServerSocketje akceptovat() metoda naslouchání na nakonfigurovaném portu pro připojení klienta.
  3. Když se klient připojí k serveru, akceptovat() metoda vrací a Zásuvka prostřednictvím kterého může server komunikovat s klientem. To je stejné Zásuvka třída, kterou jsme použili pro našeho klienta, takže postup je stejný: získejte InputStream číst od klienta a Výstupní proud napište klientovi.
  4. Pokud váš server musí být škálovatelný, budete chtít předat Zásuvka do jiného vlákna ke zpracování, aby váš server mohl pokračovat v naslouchání dalších připojení.
  5. Zavolej ServerSocketje akceptovat() znovu poslouchat další připojení.

Jak brzy uvidíte, zacházení NIO s tímto scénářem by bylo trochu jiné. Prozatím však můžeme přímo vytvořit a ServerSocket předáním portu, který chcete poslouchat (více o ServerSocketFactorys v další části):

 ServerSocket serverSocket = nový ServerSocket (port); 

A nyní můžeme přijímat příchozí připojení přes akceptovat() metoda:

 Socket socket = serverSocket.accept (); // Zpracování připojení ... 

Vícevláknové programování s paticemi Java

Výpis 2 níže vloží veškerý kód serveru tak daleko do mírně robustnějšího příkladu, který používá vlákna ke zpracování více požadavků. Zobrazený server je echo server, což znamená, že odráží zpět veškerou přijatou zprávu.

I když příklad ve výpisu 2 není komplikovaný, předpokládá něco, co přijde v další části NIO. Zvláštní pozornost věnujte množství podprocesního kódu, který musíme napsat, abychom mohli vytvořit server, který zvládne několik současných požadavků.

Výpis 2. SimpleSocketServer.java

balíček com.geekcap.javaworld.simplesocketclient; import java.io.BufferedReader; import java.io.I / OException; importovat java.io.InputStreamReader; import java.io.PrintWriter; import java.net.ServerSocket; import java.net.Socket; veřejná třída SimpleSocketServer rozšiřuje vlákno {private ServerSocket serverSocket; soukromý int port; private boolean running = false; public SimpleSocketServer (int port) {this.port = port; } public void startServer () {try {serverSocket = new 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"); // Voláním accept () pro přijetí dalšího připojení Socket socket = serverSocket.accept (); // Předat soket vláknu RequestHandler pro zpracování 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žití: SimpleSocketServer"); System.exit (0); } int port = Integer.parseInt (args [0]); System.out.println ("Spustit server na portu:" + port); SimpleSocketServer server = nový SimpleSocketServer (port); server.startServer (); // Automatické vypnutí za 1 minutu try {Thread.sleep (60000); } catch (Výjimka e) {e.printStackTrace (); } server.stopServer (); }} třída RequestHandler rozšiřuje Thread {private Socket socket; RequestHandler (Socket socket) {this.socket = socket; } @Override public void run () {try {System.out.println ("Received a connection"); // Získejte vstupní a výstupní proudy BufferedReader in = new BufferedReader (nový InputStreamReader (socket.getInputStream ())); PrintWriter out = nový PrintWriter (socket.getOutputStream ()); // Vypíšeme naši hlavičku klientovi out.println ("Echo Server 1.0"); out.flush (); // Echo lines back to the client until the client closes the connection or we receive an empty line String line = in.readLine (); while (line! = null && line.length ()> 0) {out.println ("Echo:" + line); out.flush (); line = in.readLine (); } // Uzavřete naše připojení in.close (); out.close (); socket.close (); System.out.println ("Připojení uzavřeno"); } catch (Výjimka e) {e.printStackTrace (); }}}