From 33613a85afc4b1481367fbe92a17ee59c240250b Mon Sep 17 00:00:00 2001 From: Sven Eisenhauer Date: Fri, 10 Nov 2023 15:11:48 +0100 Subject: add new repo --- .../hjp5/html/k100294.html | 824 +++++++++++++++++++++ 1 file changed, 824 insertions(+) create mode 100644 Master/Reference Architectures and Patterns/hjp5/html/k100294.html (limited to 'Master/Reference Architectures and Patterns/hjp5/html/k100294.html') diff --git a/Master/Reference Architectures and Patterns/hjp5/html/k100294.html b/Master/Reference Architectures and Patterns/hjp5/html/k100294.html new file mode 100644 index 0000000..5098e52 --- /dev/null +++ b/Master/Reference Architectures and Patterns/hjp5/html/k100294.html @@ -0,0 +1,824 @@ + + + +Handbuch der Java-Programmierung, 5. Auflage + + + + + + + + + +
 Titel  + Inhalt  + Suchen  + Index  + DOC  +Handbuch der Java-Programmierung, 5. Auflage +
 <<  +  <   +  >   + >>  + API  +Kapitel 46 - Netzwerkprogrammierung +
+
+ + + + +

46.2 Client-Sockets

+
+ +
+ + + + +

46.2.1 Adressierung

+ +

+Zur Adressierung von Rechnern im Netz wird die Klasse InetAddress +des Pakets java.net +verwendet. Ein InetAddress-Objekt +enthält sowohl eine IP-Adresse als auch den symbolischen Namen +des jeweiligen Rechners. Die beiden Bestandteile können mit den +Methoden getHostName +und getHostAddress +abgefragt werden. Mit Hilfe von getAddress +kann die IP-Adresse auch direkt als byte-Array +mit vier Elementen beschafft werden: +

+ + + + + +
+ +
+String getHostName()
+
+String getHostAddress()
+
+byte[] getAddress()
+
+
+
+java.net.InetAddress
+ +

+Um ein InetAddress-Objekt +zu generieren, stehen die beiden statischen Methoden getByName +und getLocalHost +zur Verfügung: +

+ + + + + +
+ +
+public static InetAddress getByName(String host)
+  throws UnknownHostException
+
+public static InetAddress getLocalHost()
+  throws UnknownHostException
+
+
+
+java.net.InetAddress
+ +

+getByName +erwartet einen String mit der IP-Adresse oder dem Namen des Hosts +als Argument, getLocalHost +liefert ein InetAddress-Objekt +für den eigenen Rechner. Beide Methoden lösen eine Ausnahme +des Typs UnknownHostException +aus, wenn die Adresse nicht ermittelt werden kann. Das ist insbesondere +dann der Fall, wenn kein DNS-Server zur Verfügung steht, der +die gewünschte Namensauflösung erledigen könnte (beispielsweise +weil die Dial-In-Verbindung zum Provider gerade nicht besteht). + +

+Das folgende Listing zeigt ein einfaches Programm, das zu einer IP-Adresse +den symbolischen Namen des zugehörigen Rechners ermittelt und +umgekehrt: + + +

+ + + + + +
+ +
+001 /* Listing4601.java */
+002 
+003 import java.net.*;
+004 
+005 public class Listing4601
+006 {
+007   public static void main(String[] args)
+008   {
+009     if (args.length != 1) {
+010       System.err.println("Usage: java Listing4601 <host>");
+011       System.exit(1);
+012     }
+013     try {
+014       //Get requested address
+015       InetAddress addr = InetAddress.getByName(args[0]);
+016       System.out.println(addr.getHostName());
+017       System.out.println(addr.getHostAddress());
+018     } catch (UnknownHostException e) {
+019       System.err.println(e.toString());
+020       System.exit(1);
+021     }
+022   }
+023 }
+
+
+Listing4601.java
+ +Listing 46.1: IP-Adressenauflösung

+ +

+Wird das Programm mit localhost +als Argument aufgerufen, ist seine Ausgabe: + +

+localhost
+127.0.0.1
+
+ +

+ + + + + + + + + +
+ +

+localhost ist eine Pseudo-Adresse für +den eigenen Host. Sie ermöglicht das Testen von Netzwerkanwendungen, +auch wenn keine wirkliche Netzwerkverbindung besteht (TCP/IP muss +allerdings korrekt installiert sein). Sollen wirkliche Adressen verarbeitet +werden, muss natürlich eine Verbindung zum Netz (insbesondere +zum DNS-Server) aufgebaut werden können.

+ + + + +
 Hinweis 
+
+ +

+Die nachfolgende Ausgabe zeigt die Ausgabe des Beispielprogramms, +wenn es nacheinander mit den Argumenten java.sun.com, +www.gkrueger.com und www.addison-wesley.de +aufgerufen wird: + +

+java.sun.com
+192.18.97.71
+
+www.gkrueger.com
+213.221.123.45
+
+www.addison-wesley.de
+194.163.213.76
+
+ + + + + +

46.2.2 Aufbau einer einfachen Socket-Verbindung

+ +

+Als Socket bezeichnet man eine streambasierte +Programmierschnittstelle zur Kommunikation zweier Rechner in einem +TCP/IP-Netz. Sockets wurden Anfang der achtziger Jahre für die +Programmiersprache C entwickelt und mit Berkeley UNIX 4.1/4.2 allgemein +eingeführt. Das Übertragen von Daten über eine Socket-Verbindung +ähnelt dem Zugriff auf eine Datei: +

+ +

+Während die Socket-Programmierung in C eine etwas mühsame +Angelegenheit war, ist es in Java recht einfach geworden. Im wesentlichen +sind dazu die beiden Klassen Socket +und ServerSocket +erforderlich. Sie repräsentieren Sockets aus der Sicht einer +Client- bzw. Server-Anwendung. Nachfolgend wollen wir uns mit den +Client-Sockets beschäftigen, die Klasse ServerSocket +wird im nächsten Abschnitt behandelt. + +

+Die Klasse Socket +besitzt verschiedene Konstruktoren, mit denen ein neuer Socket erzeugt +werden kann. Die wichtigsten von ihnen sind: +

+ + + + + +
+ +
+public Socket(String host, int port)
+  throws UnknownHostException, IOException
+
+public Socket(InetAddress address, int port)
+  throws IOException
+
+
+
+java.net.Socket
+ +

+Beide Konstruktoren erwarten als erstes Argument die Übergabe +des Hostnamens, zu dem eine Verbindung aufgebaut werden soll. Dieser +kann entweder als Domainname in Form eines Strings oder als Objekt +des Typs InetAddress +übergeben werden. Soll eine Adresse mehrfach verwendet werden, +ist es besser, die zweite Variante zu verwenden. In diesem Fall kann +das übergebene InetAddress-Objekt +wiederverwendet werden, und die Adressauflösung muss nur einmal +erfolgen. Wenn der Socket nicht geöffnet werden konnte, gibt +es eine Ausnahme des Typs IOException +bzw. UnknownHostException +(wenn das angegebene Zielsystem nicht angesprochen werden konnte). + +

+Der zweite Parameter des Konstruktors ist die Portnummer. Wie in Abschnitt 46.1.4 +erwähnt, dient sie dazu, den Typ des Servers zu bestimmen, mit +dem eine Verbindung aufgebaut werden soll. Die wichtigsten Standard-Portnummern +sind in Tabelle 46.2 aufgelistet. + +

+Nachdem die Socket-Verbindung erfolgreich aufgebaut wurde, kann mit +den beiden Methoden getInputStream +und getOutputStream +je ein Stream zum Empfangen und Versenden von Daten beschafft werden: +

+ + + + + +
+ +
+public InputStream getInputStream()
+  throws IOException
+
+public OutputStream getOutputStream()
+  throws IOException
+
+
+
+java.net.Socket
+ +

+Diese Streams können entweder direkt verwendet oder mit Hilfe +der Filterstreams in einen bequemer zu verwendenden Streamtyp geschachtelt +werden. Nach Ende der Kommunikation sollten sowohl die Eingabe- und +Ausgabestreams als auch der Socket selbst mit close +geschlossen werden. + +

+Als erstes Beispiel wollen wir uns ein Programm ansehen, das eine +Verbindung zum DayTime-Service auf +Port 13 herstellt. Dieser Service läuft auf fast allen UNIX-Maschinen +und kann gut zu Testzwecken verwendet werden. Nachdem der Client die +Verbindung aufgebaut hat, sendet der DayTime-Server einen String mit +dem aktuellen Datum und der aktuellen Uhrzeit und beendet dann die +Verbindung. + + +

+ + + + + +
+ +
+001 /* Listing4602.java */
+002 
+003 import java.net.*;
+004 import java.io.*;
+005 
+006 public class Listing4602
+007 {
+008   public static void main(String[] args)
+009   {
+010     if (args.length != 1) {
+011       System.err.println("Usage: java Listing4602 <host>");
+012       System.exit(1);
+013     }
+014     try {
+015       Socket sock = new Socket(args[0], 13); 
+016       InputStream in = sock.getInputStream();
+017       int len;
+018       byte[] b = new byte[100];
+019       while ((len = in.read(b)) != -1) {
+020         System.out.write(b, 0, len);
+021       }
+022       in.close();
+023       sock.close();
+024     } catch (IOException e) {
+025       System.err.println(e.toString());
+026       System.exit(1);
+027     }
+028   }
+029 }
+
+
+Listing4602.java
+ +Listing 46.2: Abfrage des DayTime-Services

+ +

+Das Programm erwartet einen Hostnamen als Argument und gibt diesen +an den Konstruktor von Socket +weiter, der eine Verbindung zu diesem Host auf Port 13 erzeugt. Nachdem +der Socket steht, wird der InputStream +beschafft. Das Programm gibt dann so lange die vom Server gesendeten +Daten aus, bis durch den Rückgabewert -1 angezeigt wird, dass +keine weiteren Daten gesendet werden. Nun werden der Eingabestream +und der Socket geschlossen und das Programm beendet. Die Ausgabe des +Programms ist beispielsweise: + +

+Sat Nov  7 22:58:37 1998
+
+ +

+ + + + + + + + + + + +
+ +

+Um in Listing 46.2 den Socket +alternativ mit einem InetAddress-Objekt +zu öffnen, wäre Zeile 015 +durch den folgenden Code zu ersetzen: + +

+InetAddress addr = InetAddress.getByName(args[0]);
+Socket sock = new Socket(addr, 13);
+
+ +
+ + + + +
 Tipp 
+
+ + + + +

46.2.3 Lesen und Schreiben von Daten

+ +

+Nachdem wir jetzt wissen, wie man lesend auf einen Socket zugreift, +wollen wir in diesem Abschnitt auch den schreibenden Zugriff vorstellen. +Dazu schreiben wir ein Programm, das eine Verbindung zum ECHO-Service +auf Port 7 herstellt. Das Programm liest so lange die Eingaben des +Anwenders und sendet sie an den Server, bis das Kommando QUIT +eingegeben wird. Der Server liest die Daten zeilenweise und sendet +sie unverändert an unser Programm zurück, von dem sie auf +dem Bildschirm ausgegeben werden. Um Lese- und Schreibzugriffe zu +entkoppeln, verwendet das Programm einen separaten Thread, der die +eingehenden Daten liest und auf dem Bildschirm ausgibt. Dieser läuft +unabhängig vom Vordergrund-Thread, in dem die Benutzereingaben +abgefragt und an den Server gesendet werden. + + +

+ + + + + +
+ +
+001 /* EchoClient.java */
+002 
+003 import java.net.*;
+004 import java.io.*;
+005 
+006 public class EchoClient
+007 {
+008   public static void main(String[] args)
+009   {
+010     if (args.length != 1) {
+011       System.err.println("Usage: java EchoClient <host>");
+012       System.exit(1);
+013     }
+014     try {
+015       Socket sock = new Socket(args[0], 7);
+016       InputStream in = sock.getInputStream();
+017       OutputStream out = sock.getOutputStream();
+018       //Timeout setzen
+019       sock.setSoTimeout(300); 
+020       //Ausgabethread erzeugen
+021       OutputThread th = new OutputThread(in);
+022       th.start();
+023       //Schleife für Benutzereingaben
+024       BufferedReader conin = new BufferedReader(
+025                              new InputStreamReader(System.in));
+026       String line = "";
+027       while (true) { 
+028         //Eingabezeile lesen
+029         line = conin.readLine();
+030         if (line.equalsIgnoreCase("QUIT")) {
+031           break;
+032         }
+033         //Eingabezeile an ECHO-Server schicken
+034         out.write(line.getBytes());
+035         out.write('\r');
+036         out.write('\n');
+037         //Ausgabe abwarten
+038         th.yield(); 
+039       }
+040       //Programm beenden
+041       System.out.println("terminating output thread...");
+042       th.requestStop();
+043       th.yield();
+044       try {
+045         Thread.sleep(1000);
+046       } catch (InterruptedException e) {
+047       }
+048       in.close(); 
+049       out.close();
+050       sock.close();
+051     } catch (IOException e) {
+052       System.err.println(e.toString());
+053       System.exit(1);
+054     }
+055   }
+056 }
+057 
+058 class OutputThread
+059 extends Thread
+060 {
+061   InputStream in;
+062   boolean     stoprequested;
+063 
+064   public OutputThread(InputStream in)
+065   {
+066     super();
+067     this.in = in;
+068     stoprequested = false;
+069   }
+070 
+071   public synchronized void requestStop()
+072   {
+073     stoprequested = true;
+074   }
+075 
+076   public void run()
+077   {
+078     int len;
+079     byte[] b = new byte[100];
+080     try {
+081       while (!stoprequested) { 
+082         try {
+083           if ((len = in.read(b)) == -1) { 
+084             break;
+085           }
+086           System.out.write(b, 0, len);
+087         } catch (InterruptedIOException e) { 
+088           //nochmal versuchen
+089         }
+090       }
+091     } catch (IOException e) {
+092       System.err.println("OutputThread: " + e.toString());
+093     }
+094   }
+095 }
+
+
+EchoClient.java
+ +Listing 46.3: Lesender und schreibender Zugriff auf einen Socket

+ +

+Eine Beispielsession mit dem Programm könnte etwa so aussehen +(Benutzereingaben sind fettgedruckt): + +

+guido_k@pc1:/home/guido_k/nettest > java EchoClient localhost
+hello
+hello
+world
+world
+12345
+12345
+quit
+closing output thread...
+
+ + +

+Wie im vorigen Beispiel wird zunächst ein Socket zu dem als Argument +angegebenen Host geöffnet. Das Programm beschafft dann Ein- und +Ausgabestreams zum Senden und Empfangen von Daten. Der Aufruf von +setSoTimeout +gibt die maximale Wartezeit bei einem lesenden Zugriff auf den Socket +an (300 ms.). Wenn bei einem read +auf den InputStream +nach Ablauf dieser Zeit noch keine Daten empfangen wurden, terminiert +die Methode mit einer InterruptedIOException; +wir kommen darauf gleich zurück. Nun erzeugt das Programm den +Lesethread und übergibt ihm den Eingabestream. In der nun folgenden +Schleife (Zeile 027) +werden so lange Eingabezeilen gelesen und an den Server gesendet, +bis der Anwender das Programm mit QUIT +beendet. +

+ + + + + + + + + + + +
+ +

+Das Programm wurde auf einer LINUX-Version entwickelt, die noch kein +präemptives Multithreading unterstützt. Die verschiedenen +Aufrufe von yield +dienen dazu, die Kontrolle an den Lesethread zu übergeben. Ohne +diesen Aufruf würde der Lesethread gar nicht zum Zuge kommen +und das Programm würde keine Daten vom Socket lesen. Auf Systemen, +die präemptives Multithreading unterstützen, sind diese +Aufrufe nicht notwendig.

+ + + + +
 Warnung 
+
+ +

+Die Klasse OutputThread implementiert +den Thread zum Lesen und Ausgeben der Daten. Da die Methode stop +der Klasse Thread +im JDK 1.2 als deprecated +markiert wurde, müssen wir mit Hilfe der Variable stoprequested +etwas mehr Aufwand treiben, um den Thread beenden zu können. +stoprequested steht normalerweise +auf false +und wird beim Beenden des Programms durch Aufruf von requestStop +auf true +gesetzt. In der Hauptschleife des Threads wird diese Variable periodisch +abgefragt, um die Schleife bei Bedarf abbrechen zu können (Zeile 081). + +

+Problematisch bei dieser Technik ist lediglich, dass der Aufruf von +read +normalerweise so lange blockiert, bis weitere Zeichen verfügbar +sind. Steht das Programm also in Zeile 083, +so hat ein Aufruf requestStop +zunächst keine Wirkung. Da das Hauptprogramm in Zeile 048 +die Streams und den Socket schließt, würde es zu einer +SocketException +kommen. Unser Programm verhindert das durch den Aufruf von setSoTimeout +in Zeile 019. Dadurch +wird ein Aufruf von read +nach spätestens 300 ms. mit einer InterruptedIOException +beendet. Diese Ausnahme wird in Zeile 087 +abgefangen, um anschließend vor dem nächsten Schleifendurchlauf +die Variable stoprequested erneut +abzufragen. + + + + +

46.2.4 Zugriff auf einen Web-Server

+ +

+Die Kommunikation mit einem Web-Server erfolgt über das HTTP-Protokoll, +wie es in den RFCs 1945 und 2068 beschrieben wurde. Ein Web-Server +läuft normalerweise auf TCP-Port 80 (manchmal läuft er zusätzlich +auch auf dem UDP-Port 80) und kann wie jeder andere Server über +einen Client-Socket angesprochen werden. Wir wollen an dieser Stelle +nicht auf Details eingehen, sondern nur die einfachste und wichtigste +Anwendung eines Web-Servers zeigen, nämlich das Übertragen +einer Seite. Ein Web-Server ist in seinen Grundfunktionen ein recht +einfaches Programm, dessen Hauptaufgabe darin besteht, angeforderte +Seiten an seine Clients zu versenden. Kompliziert wird er vor allem +durch die Vielzahl der mittlerweile eingebauten Zusatzfunktionen, +wie beispielsweise Logging, Server-Scripting, Server-Side-Includes, +Security- und Tuning-Features usw. + +

+Fordert ein Anwender in seinem Web-Browser eine Seite an, so wird +diese Anfrage vom Browser als GET-Transaktion +an den Server geschickt. Um beispielsweise die Seite http://www.javabuch.de/index.html +zu laden, wird folgendes Kommando an den Server www.javabuch.de +gesendet: + +

+GET /index.html
+
+ + +

+Der erste Teil gibt den Kommandonamen an, dann folgt die gewünschte +Datei. Die Zeile muss mit einer CRLF-Sequenz abgeschlossen werden, +ein einfaches '\n' reicht nicht aus. Der Server versucht nun die angegebene +Datei zu laden und überträgt sie an den Client. Ist der +Client ein Web-Browser, wird er den darin befindlichen HTML-Code interpretieren +und auf dem Bildschirm anzeigen. Befinden sich in der Seite Verweise +auf Images, Applets oder Frames, so fordert der Browser die fehlenden +Seiten in weiteren GET-Transaktionen von deren Servern ab. + +

+Die Struktur des GET-Kommandos wurde mit der Einführung von HTTP +1.0 etwas erweitert. Zusätzlich werden nun am Ende der Zeile +eine Versionskennung und wahlweise in den darauffolgenden Zeilen weitere +Headerzeilen mit Zusatzinformationen mitgeschickt. Nachdem die letzte +Headerzeile gesendet wurde, folgt eine leere Zeile (also ein alleinstehendes +CRLF), um das Kommandoende anzuzeigen. HTTP 1.0 ist weit verbreitet, +und das obige Kommando würde von den meisten Browsern in folgender +Form gesendet werden (jede der beiden Zeilen muss mit CRLF abgeschlossen +werden): + +

+GET /index.html HTTP/1.0
+
+
+ + +

+Wird HTTP/1.0 verwendet, ist auch die Antwort des Servers etwas komplexer. +Anstatt lediglich den Inhalt der Datei zu senden, liefert der Server +seinerseits einige Headerzeilen mit Zusatzinformationen, wie beispielsweise +den Server-Typ, das Datum der letzten Änderung oder den MIME-Typ +der Datei. Auch hier ist jede Headerzeile mit einem CRLF abgeschlossen, +und nach der letzten Headerzeile folgt eine Leerzeile. Erst dann beginnt +der eigentliche Dateiinhalt. + +

+Das folgende Programm kann dazu verwendet werden, eine Datei von einem +Web-Server zu laden. Es wird mit einem Host- und einem Dateinamen +als Argument aufgerufen und lädt die Seite vom angegebenen Server. +Das Ergebnis wird (mit allen Headerzeilen) auf dem Bildschirm angezeigt. + + +

+ + + + + +
+ +
+001 /* Listing4604.java */
+002 
+003 import java.net.*;
+004 import java.io.*;
+005 
+006 public class Listing4604
+007 {
+008   public static void main(String[] args)
+009   {
+010     if (args.length != 2) {
+011       System.err.println(
+012         "Usage: java Listing4604 <host> <file>"
+013       );
+014       System.exit(1);
+015     }
+016     try {
+017       Socket sock = new Socket(args[0], 80);
+018       OutputStream out = sock.getOutputStream();
+019       InputStream in = sock.getInputStream();
+020       //GET-Kommando senden
+021       String s = "GET " + args[1] + " HTTP/1.0" + "\r\n\r\n";
+022       out.write(s.getBytes());
+023       //Ausgabe lesen und anzeigen
+024       int len;
+025       byte[] b = new byte[100];
+026       while ((len = in.read(b)) != -1) {
+027         System.out.write(b, 0, len);
+028       }
+029       //Programm beenden
+030       in.close();
+031       out.close();
+032       sock.close();
+033     } catch (IOException e) {
+034       System.err.println(e.toString());
+035       System.exit(1);
+036     }
+037   }
+038 }
+
+
+Listing4604.java
+ +Listing 46.4: Laden einer Seite von einem Web-Server

+ +

+Wird das Programm beispielsweise auf einem SUSE-Linux 5.2 mit frisch +installiertem Apache-Server mit localhost +und /index.html als Argument +aufgerufen, so beginnt seine Ausgabe wie folgt: + +

+HTTP/1.1 200 OK
+Date: Sun, 08 Nov 1998 18:26:13 GMT
+Server: Apache/1.2.5 S.u.S.E./5.1
+Last-Modified: Sun, 24 May 1998 00:46:46 GMT
+ETag: "e852-45c-35676df6"
+Content-Length: 1116
+Accept-Ranges: bytes
+Connection: close
+Content-Type: text/html
+
+<HTML>
+<HEAD>
+<TITLE>Apache HTTP Server - Beispielseite</TITLE>
+</HEAD>
+<BODY bgcolor=#ffffff>
+<H1> Der Apache WWW Server </H1> <BR>
+Diese Seite soll nur als Beispiel dienen.
+Die <A HREF="./manual/">Dokumentation zum
+Apache-Server</A> finden Sie hier.
+<P>
+...
+
+ +
+ + + +
 Titel  + Inhalt  + Suchen  + Index  + DOC  +Handbuch der Java-Programmierung, 5. Auflage, Addison +Wesley, Version 5.0.1 +
 <<  +  <   +  >   + >>  + API  +© 1998, 2007 Guido Krüger & Thomas +Stark, http://www.javabuch.de +
+ + + -- cgit v1.2.3