Titel   Inhalt   Suchen   Index   DOC  Handbuch der Java-Programmierung, 7. Auflage
 <<    <     >    >>   API  Kapitel 48 - Netzwerkprogrammierung

48.2 Client-Sockets



48.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 /* Listing4801.java */
002 
003 import java.net.*;
004 
005 public class Listing4801
006 {
007   public static void main(String[] args)
008   {
009     if (args.length != 1) {
010       System.err.println("Usage: java Listing4801 <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 }
Listing4801.java
Listing 48.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

48.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 48.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 48.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 /* Listing4802.java */
002 
003 import java.net.*;
004 import java.io.*;
005 
006 public class Listing4802
007 {
008   public static void main(String[] args)
009   {
010     if (args.length != 1) {
011       System.err.println("Usage: java Listing4802 <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 }
Listing4802.java
Listing 48.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 48.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);
 Tip 

48.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       }
038       //Programm beenden
039       System.out.println("terminating output thread...");
040       th.requestStop();
041       try {
042         Thread.sleep(1000);
043       } catch (InterruptedException e) {
044       }
045       in.close(); 
046       out.close();
047       sock.close();
048     } catch (IOException e) {
049       System.err.println(e.toString());
050       System.exit(1);
051     }
052   }
053 }
054 
055 class OutputThread
056 extends Thread
057 {
058   InputStream in;
059   boolean     stoprequested;
060 
061   public OutputThread(InputStream in)
062   {
063     super();
064     this.in = in;
065     stoprequested = false;
066   }
067 
068   public synchronized void requestStop()
069   {
070     stoprequested = true;
071   }
072 
073   public void run()
074   {
075     int len;
076     byte[] b = new byte[100];
077     try {
078       while (!stoprequested) { 
079         try {
080           if ((len = in.read(b)) == -1) { 
081             break;
082           }
083           System.out.write(b, 0, len);
084         } catch (InterruptedIOException e) { 
085           //nochmal versuchen
086         }
087       }
088     } catch (IOException e) {
089       System.err.println("OutputThread: " + e.toString());
090     }
091   }
092 }
EchoClient.java
Listing 48.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.

Die Klasse OutputThread implementiert den Thread zum Lesen und Ausgeben der Daten. Da die Methode stop der Klasse Thread 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 078).

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 080, so hat ein Aufruf requestStop zunächst keine Wirkung. Da das Hauptprogramm in Zeile 045 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 084 abgefangen, um anschließend vor dem nächsten Schleifendurchlauf die Variable stoprequested erneut abzufragen.

48.2.4 Zugriff auf einen Webserver

Die Kommunikation mit einem Webserver erfolgt über das HTTP-Protokoll, wie es in den RFCs 1945 und 2068 beschrieben wurde. Ein Webserver 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 Webservers zeigen, nämlich das Übertragen einer Seite. Ein Webserver 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 mit Hilfe des HTTP 1.0-Protokolls von einem Webserver 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. Anpassungen, die für das gebräuchliche HTTP 1.1-Protokoll erforderlich sind, werden im nächsten Abschnitt demonstriert, während sich der übernächste Abschnitt schließlich mit dem komfortablen Zugriff über die Klasse java.net.URL beschäftigt.

001 /* Listing4804.java */
002 
003 import java.net.*;
004 import java.io.*;
005 
006 public class Listing4804
007 {
008   public static void main(String[] args)
009   {
010     if (args.length != 2) {
011       System.err.println(
012         "Usage: java Listing4804 <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[4096];
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 }
Listing4804.java
Listing 48.4: Laden einer Seite von einem Webserver (HTTP 1.0)

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>
...

Anpassungen für das HTTP 1.1-Protokoll

Das am CERN in Genf entwickelte Protokoll HTTP 1.0 standardisierte den netzwerkbasierenden Zugriff auf entfernte Dokumente. Filehoster und multiple URLs auf ein und dieselbe IP-Adresse wurden während der Entwurfsphase des Protokolls allerdings noch nicht vorgesehen und erst in der nachfolgenden Version HTTP 1.1 berücksichtigt. Dieser Abschnitt beschreibt die Anpassungen, die vorgenommen werden müssen, um Dokumente über einen Socket von einem HTTP 1.1-Server herunterzuladen, doch zunächst ein wenig Motivation.

Eventuell sind Sie ja bereits stolzer Besitzer einer eigenen Domain und hosten diese bei einem Anbieter wie Strato, 1und1 etc. Dies bedeutet, dass Sie zwar rechtlicher Inhaber Ihrer Domain sind, alle Aufrufe über das Internet jedoch auf die Server Ihres Webhosters umgeleitet und die Dokumente von dort heruntergeladen werden. Abhängig von Ihrem Vertrag stellt Ihnen Ihr Webhoster dabei einen dedizierten (virtuellen) Server zur Verfügung oder lässt die Anfrage von einem Rechner beantworten, der neben Ihrer Domain auch für eine Reihe weiterer Domains verantwortlich ist. Dieses Konzept wird auch als Virtueller Host bezeichnet. Daraus kann sich aber nun folgendes Problem ergeben: Angenommen, Sie sind der Besitzer der Domain abc.de und hosten diese auf dem gleichen Server wie der Inhaber der Domain xyz.de. Außerdem beinhalten beide Internetauftritte das Dokument index.html. In dieser Konstellation wird der Server nun über seine IP-Netzwerkadresse angesprochen und aufgefordert, die Ressource index.html zu übermitteln. Da diese jedoch doppelt vorhanden ist, benötigt der Server zusätzlich die Information, unter welcher URL die betreffende Ressource zu finden sein soll.

Um das oben geschilderte Problem zu lösen, wurde mit HTTP 1.1 der zusätzliche Request-Header Host eingeführt, dessen Wert die angeforderte Domain enthält. Auf diese Weise kann das Konzept der virtuellen Hosts realisiert werden, da der Server die angeforderten Ressourcen nun eindeutig auflösen kann. Das folgende Listing zeigt, wie unter Verwendung des Host-Headers über das HTTP 1.1-Protokoll auf ein Dokument der Domain www.abc.de zugegriffen werden kann.

001 /* Listing4805.java */
002 
003 import java.net.*;
004 import java.io.*;
005 
006 public class Listing4805
007 {
008   public static void main(String[] args)
009   {
010     if (args.length != 2) {
011       System.err.println(
012         "Usage: java Listing4805 <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 unter Verwendung des Host-Headers senden
021       String s = "GET " + args[1] + " HTTP/1.1" + "\r\n";
022       s += "Host: www.abc.de\r\n\r\n";
023       out.write(s.getBytes());
024       //Ausgabe lesen und anzeigen
025       int len;
026       byte[] b = new byte[4096];
027       while ((len = in.read(b)) != -1) {
028         System.out.write(b, 0, len);
029       }
030       //Programm beenden
031       in.close();
032       out.close();
033       sock.close();
034     } catch (IOException e) {
035       System.err.println(e.toString());
036       System.exit(1);
037     }
038   }
039 }
Listing4805.java
Listing 48.5: Laden einer Seite von einem Webserver (HTTP 1.1)

Zugriff auf einen Webserver unter Verwendung der Klasse URL

Die beiden vorangegangenen Beispiele demonstrieren die Arbeitsweise von Sockets am Beispiel des Zugriffs auf eine Internetressource. Dabei wurde, insbesondere beim Zugriff auf einen virtuellen Host, zusätzliches Wissen über das Protokoll HTTP benötigt.

Um den Zugriff auf eine HTTP-Ressource zu vereinfachen, stellt Java die Klasse URL aus dem Paket java.net zur Verfügung. Diese übernimmt per Konstruktor die Informationen über Host, Port, Protokoll und die angeforderte Ressource und öffnet über die Methode openStream() einen InputStream, über den die Daten abgerufen werden können. Das folgende Listing demonstriert den Zugriff auf eine Internetressource mit der Klasse java.net.URL.

001 /* Listing4806.java */
002 
003 import java.net.*;
004 import java.io.*;
005 
006 public class Listing4806
007 {
008   public static void main(String[] args)
009   {
010     if (args.length != 2) {
011       System.err.println(
012         "Usage: java Listing4806 <host> <file>"
013       );
014       System.exit(1);
015     }
016     
017     try {
018       // Aufbau der Connection
019       URL url = new URL("http", args[0], 80, args[1]);
020 
021       InputStream in = url.openStream();
022       int len;
023       byte[] b = new byte[4096];
024       while ((len = in.read(b)) != -1) {
025         System.out.write(b, 0, len);
026       }
027       
028       //Programm beenden
029       in.close();
030     } catch (IOException e) {
031       System.err.println(e.toString());
032       System.exit(1);
033     }
034   }
035 }
Listing4806.java
Listing 48.6: Laden einer Seite von einem Webserver über java.net.URL

Die vorangegangenen drei Abschnitte haben Ihnen gezeigt, wie Sie mit Hilfe eines Socket und unter stückweiser Implementierung des HTTP-Protokolls sowie unter Verwendung der Klasse java.net.URL auf die Dateien eines Webservers zugreifen. Der nächste Abschnitt demonstriert Ihnen nun, wie Sie das Gegenstück, also einen rudimentären Webserver, in Java implementieren können.


 Titel   Inhalt   Suchen   Index   DOC  Handbuch der Java-Programmierung, 7. Auflage, Addison Wesley, Version 7.0
 <<    <     >    >>   API  © 1998, 2011 Guido Krüger & Heiko Hansen, http://www.javabuch.de