Galileo Computing < openbook >
Galileo Computing - Professionelle Buecher. Auch fuer Einsteiger.
Galileo Computing - Professionelle Buecher. Auch fuer Einsteiger.


Java ist auch eine Insel von Christian Ullenboom
Buch: Java ist auch eine Insel (Galileo Computing)
gp Kapitel 16 Netzwerkprogrammierung
gp 16.1 Grundlegende Begriffe
gp 16.1.1 Internet-Standards und RFC
gp 16.2 URL-Verbindungen und URL-Objekte
gp 16.2.1 Die Klasse URL
gp 16.2.2 Informationen über eine URL
gp 16.2.3 Der Zugriff auf die Daten über die Klasse URL
gp 16.2.4 Verbindungen durch einen Proxy-Server
gp 16.3 Die Klasse URLConnection
gp 16.3.1 Methoden und Anwendung von URLConnection
gp 16.3.2 Protokoll- und Content-Handler
gp 16.3.3 Im Detail: vom URL zu URLConnection
gp 16.3.4 Autorisierte URL-Verbindungen mit Basic Authentication
gp 16.3.5 Apache Jakarta HttpClient
gp 16.4 Das Common Gateway Interface
gp 16.4.1 Parameter für ein CGI-Programm
gp 16.4.2 Kodieren der Parameter für CGI-Programme
gp 16.4.3 Eine Suchmaschine ansprechen
gp 16.5 Host- und IP-Adressen
gp 16.5.1 Das Netz ist Klasse ...
gp 16.5.2 IP-Adresse des lokalen Hosts
gp 16.5.3 Die Methode getAllByName()
gp 16.6 NetworkInterface
gp 16.7 IPv6 für Java mit Jipsy
gp 16.8 Socket-Programmierung
gp 16.8.1 Das Netzwerk ist der Computer
gp 16.8.2 Standarddienste unter Windows nachinstallieren
gp 16.8.3 Stream-Sockets
gp 16.8.4 Informationen über den Socket
gp 16.8.5 Mit telnet an den Ports horchen
gp 16.8.6 Ein kleines Echo - lebt der Rechner noch?
gp 16.9 Client/Server-Kommunikation
gp 16.9.1 Warten auf Verbindungen
gp 16.9.2 Ein Multiplikations-Server
gp 16.10 SLL-Verbindungen mit JSSE
gp 16.11 Web-Protokolle mit NetComponents nutzen
gp 16.12 E-Mail
gp 16.12.1 Wie eine E-Mail um die Welt geht
gp 16.12.2 Übertragungsprotokolle
gp 16.12.3 Das Simple Mail Transfer Protocol
gp 16.12.4 E-Mails versenden mit Suns JavaMail-API
gp 16.12.5 MimeMultipart-Nachrichten schicken
gp 16.12.6 E-Mails mittels POP3 abrufen
gp 16.13 Arbeitsweise eines Web-Servers
gp 16.13.1 Das Hypertext Transfer Protocol (HTTP)
gp 16.13.2 Anfragen an den Server
gp 16.13.3 Die Antworten vom Server
gp 16.14 Datagram-Sockets
gp 16.14.1 Die Klasse DatagramSocket
gp 16.14.2 Datagramme und die Klasse DatagramPacket
gp 16.14.3 Auf ein hereinkommendes Paket warten
gp 16.14.4 Ein Paket zum Senden vorbereiten
gp 16.14.5 Methoden der Klasse DatagramPacket
gp 16.14.6 Das Paket senden
gp 16.14.7 Die Zeitdienste und ein eigener Server und Client
gp 16.15 Internet Control Message Protocol (ICMP)
gp 16.15.1 Ping
gp 16.16 Multicast-Kommunikation


Galileo Computing

16.2 URL-Verbindungen und URL-Objektedowntop

Eine Verbindung über eine URL (Uniform Resource Locator) ist am leichtesten zu benutzen und zu programmieren. Eine URL (RFC 1738) ist das Adressenformat für eine Ressource im Web. Sie ist also für das Internet so etwas wie ein Dateiname für das Dateisystem. Die allgemeinste Form einer URL ist folgende:

Schema:Spezialisierung des Schemas

Nach dem RFC 1738 werden folgende Schemas unterschieden:


Schema Bedeutung des Schemas
ftp File Transfer Protocol
http Hypertext Transfer Protocol
gopher Gopher Protocol
mailto Elektronische Mail
news USENET News
nntp USENET News mit NNTP-Zugang
telnet Interaktive Session
wais Wide Area Information Servers
file Hostspezifischer Dateiname
prospero Prospero Directory Service

Tabelle 16.1 Schemas nach dem RFC 1738

Wir wollen ein Schema im Folgenden auch Protokoll nennen. Das Protokoll bestimmt die Zugriffsart, und das meistverwendete Protokoll ist mittlerweile HTTP (Hypertext Transfer Protocol), mit dem auf Inhalte des Webs zugegriffen wird. Die URL für die Dienste im Web beginnt mit »http«. Als Beispiel soll http://www.java-tutor.com/seminare/servlets.html dienen. Die URL enthält den Namen des benutzten Schemas (http), und nach einem Doppelpunkt folgt der Servername und ein Pfad auf das Verzeichnis. Sun unterstützt im Java-SDK die zusätzlichen Protokolle »doc«, »file«, »ftp«, »gopher«, »http«, »jar«, »mailto«, »system«, »verbatim«. Unterschiedliche Implementierungen haben mehr oder weniger unterstützte Protokolle. Eigene Protokolle lassen sich leicht selbst implementieren.

Während die Syntax für oben nicht genannte Adressierungen schwankt, lässt sich für die IP-basierten Protokolle (IP für »Internet Protocol«) eine Syntax für den schemenspezifischen Teil ausmachen. Dieser beginnt nach einem Doppel-Slash. Somit ist deutlich, dass diese Angabe dem Internet-Schema folgt:

//user:password@host:port/filepath#ref

Einige oder alle Teile können bei einer URL ausgelassen werden. So sind »user:password@«, »:password«, »:port«, »filepath« und »ref« optional und protokollabhängig. Sind Benutzername und das Passwort angegeben, so folgt ein At-Zeichen »@«. Natürlich dürfen im Passwort und Benutzernamen Doppelpunkt, At-Zeichen oder Slash nicht vorkommen.

Die einzelnen Komponenten haben folgende Bedeutung:

gp user
Ein optionaler Benutzername. Dieser kann für den Zugriff über ftp vergeben werden.
gp password
Ein optionales Passwort. Es folgt getrennt durch einen Doppelpunkt nach dem Benutzernamen. Ein leerer Benutzername und ein leeres Passwort (etwa ftp://@host.com) sind etwas anders als kein Benutzername beziehungsweise kein Passwort (zum Beispiel ftp:// host.com/). Ein Passwort ohne Benutzername kann nicht angegeben werden. Umgekehrt natürlich schon: So hat ein ftp://oing:@host.com einen Benutzernamen, aber ein leeres Passwort.
gp host
Auf die Angabe des Protokolls folgt der Name der Domain oder die IP-Adresse des Servers. Name und IP-Adressen sind in der Regel gleichwertig, da von einem besonderen Dienst der Name in eine IP-Adresse umgesetzt wird - verlangt doch eine achtstellige IP-Adresse ein zu gutes Gedächtnis.
gp port
Eine Verbindung zu einem Rechner entsteht immer durch eine Art Tür, die Port genannt wird. Diese Port-Nummer lässt den Server die Dienste kategorisieren. Jeder Dienst bekommt eine andere Port-Nummer, damit sie sich unterscheiden lassen. Normalerweise horcht der HTTP-Server auf Port 80.
gp url-path
Auf den Servernamen folgt die Angabe der Datei, auf die wir via HTTP oder FTP zugreifen wollen. Da sie in einem Verzeichnis liegen kann, beschreibt die folgende Angabe den Weg zur Datei. Ist keine Datei vorhanden und endet die Angabe der URL mit einem Slash »/«, so versucht der Web-Server, auf eine der Dateien index.html oder index.htm zuzugreifen.

Galileo Computing

16.2.1 Die Klasse URLdowntop

Um ein URL-Objekt zu erzeugen, ist es am einfachsten, über eine String-Repräsentation der URL-Adresse zu gehen.


Beispiel Um eine Verbindung zum Host vom Java-Tutor zu erhalten, nehmen wir die bekannte URL und erzeugen damit das Objekt:
URL jtURL = new URL( ">http://java-tutor.com/index.html"; );

Die URL-Klasse besitzt noch zusätzliche Konstruktoren; diese sind dann nützlich, wenn Komponenten der Adresse, also Zugriffsart (beispielsweise das HTTP), Host-Name und Dateireferenz getrennt gegeben sind.


Beispiel Eine Alternative zur oben genannten Form ist:
URL jtURL = new URL( "http", "java-tutor.com", "index.html" );

Das zweite Argument ist die Basisadresse der URL. Was dem String im dritten Parameter übergeben wird, ist der Ressourcen-Name relativ zur Basisadresse. Ist diese Basisadresse null, was möglich ist, dann ist die zweite Angabe absolut zu nehmen. Und ist der zweite Parameter in absoluter Notation formuliert, wird alles im ersten Parameter ignoriert.

Da eine URL auch einen entfernten Rechner an einem anderen Port ansprechen kann, existiert dafür ebenfalls ein Konstruktor:

URL url = new URL( "http", "java-tutor.com", 80, "index.html" );

Die URL des Objekts wurde durch eine absolute Adresse erzeugt. Diese enthält dann alle Informationen, die für den Aufbau zum Host nötig sind. Es können jedoch auch URL-Objekte erzeugt werden, wo nur eine relative Angabe bekannt ist. Relative Angaben werden häufig bei HTML-Seiten verwendet, da die Seite so besser vor Verschiebungen geschützt ist. Damit die Erzeugung eines URL-Objekts mit relativer Adressierung gelingt, muss eine Basisadresse bekannt sein. Ein Konstruktor für relative Adressen erwartet diese Basisadresse als Parameter.


Beispiel Für die Java-Tutor-Seite nutzen wir ein URL-Objekt, welches auf die Datei index.html zeigt:
URL jtURL   = new URL( ">http://java-tutor.com"; );
URL jtIndex = new URL( jtURL, "index.html");

Diese Art und Weise der URL-Objekt-Erzeugung ist besonders praktisch für Referenzen innerhalb von Web-Seiten (Named Anchors).


Beispiel Besitzt eine Web-Seite eine Textmarke lang, so kann der URL-Konstruktor für relative URLs verwendet werden.
URL virtuellURL       = new URL( ">http://java-tutor.com"; );
URL virutellBottomURL = new URL( virtuellURL, "faq.html#lang" );

Jeder der Konstruktoren wirft eine MalformedURLException, wenn der Parameter im Konstruktor entweder null ist oder er ein unbekanntes Protokoll (wie in telepatic:\\ulli\brain\java) beschreibt. Somit ist der Code in der Regel von einem Block der folgenden Art umgeben:

try {
  URL myURL = new URL( . . . )
}
catch ( MalformedURLException e ) {
  // Fehlerbehandlung
}

Es ist wichtig zu erkennen, dass die Ausnahme nicht erzeugt wird, weil der angesprochene Rechner nicht erreicht werden kann. Nur die Schreibweise der URL ist für die Ausnahme ausschlaggebend. Internet-Verbindungen bauen die Konstruktoren nicht auf.


class java.net.URL
implements Serializable, Comparable

Abbildung
Hier klicken, um das Bild zu Vergrößern

gp URL( String protocol, String host, int port, String file )
throws MalformedURLException
Erzeugt ein URL-Objekt mit dem gegebenen Protokoll, Host-Namen, Port-Nummer und Datei. Ist die Port-Nummer -1, so wird der Standardport verwendet, zum Beispiel für das WWW ist der Port 80.
gp URL( String protocol, String host, String file )
throws MalformedURLException
Das Gleiche wie URL(protocol, host, -1, file)
gp URL( String ) throws MalformedURLException
Erzeugt ein Objekt aus der URL-Zeichenkette.
gp URL( URL, String ) throws MalformedURLException
Erzeugt relativ zur URL ein neues URL-Objekt.

Galileo Computing

16.2.2 Informationen über eine URLdowntop

Ist das URL-Objekt einmal angelegt, so lassen sich Attribute des Objekts erfragen, aber nicht mehr ändern. Es gibt zwar Setter-Methoden, aber diese sind protected und somit den Unterklassen vorbehalten. Uns normalen Klassenbenutzern bietet die URL-Klasse nur Methoden zum Zugriff. So lassen sich Protokoll, Host-Name, Port-Nummer und Dateiname mit Zugriffsmethoden erfragen. Es lassen sich jedoch nicht alle URL-Adressen so detailliert aufschlüsseln, und außerdem sind manche der Zugriffsmethoden nur für HTTP sinnvoll.

gp String getProtocol()
Liefert das Protokoll der URL.
gp String getHost()
Liefert den Host-Namen der URL, falls dies möglich ist. Für das Protokoll »file« ist dies ein leerer String.
gp int getPort()
Liefert die Port-Nummer. Ist sie nicht gesetzt, liefert getPort() eine -1.
gp String getFile()
Gibt den Dateinamen der URL zurück.
gp String getRef()
Gibt die relative Adresse der URL zurück.

final class java.net.URL
implements Serializable, Comparable

Das kleine nachfolgende Programm erzeugt ein URL-Objekt zu http://java-tutor.com. Alle Möglichkeiten zur Angabe von URL-Informationen werden genutzt. Anschließend erfolgt ein Auslesen aller Attribute.

Listing 16.1 ParseURL.java

import java.net.*;
class ParseURL
{
  public static void main( String args[] )
  {
    try
    {
      URL url = new URL( ">http://java-tutor.com/faq.html#lang";);
      System.out.println( "protocol = " + url.getProtocol() );
      System.out.println( "host = " + url.getHost() );
      System.out.println( "filename = " + url.getFile() );
      System.out.println( "port = " + url.getPort() );
      System.out.println( "ref = " + url.getRef() );
    }
    catch ( MalformedURLException e ) {
      System.out.println( "MalformedURLException: " + e );
    }
  }
}

Und dies ist die Ausgabe:

protocol = http
host = java-tutor.com
filename = /faq.html
port = -1
ref = lang

Verweisen die URLs auf die gleiche Seite?

Die Methode equals() aus der Klasse Object ist uns bekannt. Sie soll von jeder Klasse so implementiert werden, dass gleiche Objekte true zurückliefern. Jede Klasse soll aber selbst ihre Inhalte vergleichen und nicht nur ihre Objektreferenzen. So muss also die URL-Klasse untersuchen, ob alle Komponenten der einen URL mit der anderen URL übereinstimmen. equals() untersucht dafür zuerst, ob es sich bei der vergleichenden Klasse um ein Exemplar von URL handelt. Wenn ja, wird untersucht, ob die Komponenten Referenzen besitzen oder nicht. Dies wird erreicht, indem Protokoll, Host, Port und Datei untersucht werden. Hierfür bietet sich auch die öffentliche Methode sameFile() an. Ein Anker ist für den Vergleich nicht bestimmend.


final class java.net.URL
implements Serializable, Comparable

gp boolean sameFile( URL other)
Vergleicht zwei URL-Objekte. Die Methode liefert true, falls beide Objekte auf die gleiche Ressource zeigen. Der Anker der HTML-Dateien ist unwichtig.

Beispiel equals() für URL-Objekte

Listing 16.2 URLContentsTheSame.java

import java.net.*;
class URLContentsTheSame
{
  public static void main( String args[] )
  {
    try
    {
      URL sunsite = new URL(
         ">http://sunsite.unc.edu/javafaq/oldnews.html";);
      URL helios = new URL(
         ">http://helios.oit.unc.edu/javafaq/oldnews.html";);
      if ( sunsite.equals(helios) )
        System.out.println( sunsite + " = " + helios );
      else
        System.out.println( sunsite + " != " + helios );
    }
    catch ( MalformedURLException e ) {
      System.err.println(e);
    }
  }
}


Galileo Computing

16.2.3 Der Zugriff auf die Daten über die Klasse URLdowntop

Um auf die auf dem Web-Server gespeicherten Daten zuzugreifen, gibt es drei Möglichkeiten. Zwei davon nutzen Streams, und zwar einmal über die Klasse URL und einmal über eine URLConnection. Bei der dritten Möglichkeit ist Handarbeit angesagt, und deshalb wird sie im Kapitel über Sockets beschrieben.

Jedes URL-Objekt besitzt die Methode openStream(), die einen InputStream zum Weiterverarbeiten liefert, so dass wir dort die Daten auslesen können:

InputStream in = myURL.openStream();

final class java.net.URL
implements Serializable, Comparable

gp final InputStream openStream() throws IOException
Öffnet eine Verbindung zum Server und liefert einen InputStream zurück. Diese Methode ist eine Abkürzung für openConnection().getInputStream().
gp URLConnection openConnection() throws IOException
Liefert ein URLConnection-Objekt, welches die Verbindung zum entfernten Objekt vertritt. openConnection() wird vom Protokoll-Handler immer dann aufgerufen, wenn eine neue Verbindung geöffnet wird.

Verweist die URL auf eine Textdatei, dann erweitern wir oft den InputStream zu einem BufferedReader, da dieser eine readLine()-Methode besitzt. Folgender Programmcode liest so lange Zeilen, bis das Ende der Eingabe signalisiert wird. Glücklicherweise ist uns die Art des Vorgehens schon bekannt, da sich ja das Lesen von einer Datei nicht vom Lesen eines entfernten URL-Objekts unterscheidet.

Listing 16.3 OpenURLStream.java

import java.net.*;
import java.io.*;
class OpenURLStream
{
  public static void main( String args[] )
  {
    try
    {
      URL url = new URL( ">http://java-tutor.com/aufgaben/bond.txt"; );
      Reader is = new InputStreamReader( url.openStream() );
      BufferedReader in = new BufferedReader( is );
      for ( String s; ( s = in.readLine() ) != null; )
        System.out.println( s );
      
      in.close();
    }
    catch ( MalformedURLException e ) {
        System.out.println( "MalformedURLException: " + e );
    }
    catch ( IOException e ) {
        System.out.println( "IOException: " + e );
    }
  }
}

Wir erzeugen ein URL-Objekt und rufen darauf die openStream()-Methode auf. Diese liefert einen InputStream auf den Dateiinhalt. In der API-Beschreibung wurde aber schon kurz erwähnt, dass diese Funktion eigentlich nur eine Abkürzung für openConnection().getInputStream() ist. openConnection() erzeugt ein URLConnection-Objekt und sendet diesem die Nachricht getInputStream(). Sind die Daten gelesen, schließt close() den Datenstrom - close() bezieht sich allerdings nicht auf das URL-Objekt, sondern auf den Datenstrom.

Wir wollen uns im nächsten Abschnitt mit dem URLConnection-Objekt beschäftigen, denn damit wird die Verbindung über das Netzwerk zum Inhalt aufgebaut. Die URL-Klasse besitzt nur deshalb die Abkürzung über openStream(), da zum einen nicht jeder wissen muss, dass URLConnection dahinter steckt, und zweitens, weil es Tipperei erspart.

Das Beispiel zeigt auch, dass bei openConnection() ein try/catch-Block notwendig ist. Denn geht etwas daneben, zum Beispiel, wenn der Dienst nicht verfügbar ist, so wird eine IOException ausgelöst:

try
{
  URL ohoURL = new URL( ">http://www.oho.com/"; );
  ohoURL.openConnection();
}
catch ( MalformedURLException e ) {    // new URL() ging daneben
  ...
}
catch ( IOException e ) {        // openConnection() schlug fehl
  ...
}

Galileo Computing

16.2.4 Verbindungen durch einen Proxy-Servertoptop

In vielen größeren Unternehmen bekommt ein einzelner Client keinen direkten Zugriff auf das Internet. Vielmehr laufen die Verbindungen über eine Zwischenstelle, die Proxy genannt wird. Dieser cacht zum Beispiel Webseiten und erhöht damit die Performanz. Gleichzeitig ist ein Proxy Teil einer wichtigen unternehmensweiten Sicherheitsstrategie. Seiten können geloggt und ausgefiltert werden - welcher Chef möchte schon, dass seine Mitarbeiter laufend Firmenkugelschreiber auf Ebay verticken.

Java nutzt zur Festlegung der Proxy-Eigenschaften zwei beziehungsweise drei System-Properties, die unter http://java.sun.com/j2se/1.4.1/docs/guide/net/properties.html beschrieben sind.

gp http.proxyHost. Adresse des Rechners, der als Proxy dient
gp http.proxyPort. Port des Proxy-Rechners

Um nun in Applikationen einen Proxy einzusetzen, sind die Eigenschaften zu setzen; eine Möglichkeit ist über die Kommandozeile mit -D, eine andere über System.setProperty().

System.setProperty( "proxySet", "true" );
System.setProperty( "proxyHost", "myProxyHost" );
System.setProperty( "proxyPort", "myProxyPort" );

Für Applets sind diese Eigenschaften automatisch über die Browser-Einstellungen gesetzt.

Wenn der Proxy eine Autorisierung erfordert - häufig angezeigt, wenn der Client einen Fehler mit 407 (authentication required) bekommt - muss ihm ein Benutzername und Passwort übermittelt werden. Hier gibt es bedauerlicherweise eine ganze Reihe von unterschiedlichen Lösungen, abhängig von den Java-Versionen. Funktionieren sollte im Allgemeinen:

System.setProperty( "http.proxyUser", "myProxyUser" );
System.setProperty( "http.proxyPassword", "myProxyPasswort" );

Soll ein sicherer Webserver über eine Proxy-Verbindung kontaktiert werden, so lässt sich mit installierter Java Secure Socket Extension (JSSE) https.ProxyHost und https.ProxyPort passend setzen.

Im Fall von SOCKS1 sind die Eigenschaften socksProxyHost für den Server und socksProxyPort für den Port zu setzen. Der steht standardmäßig auf 1080.






1 Mit SOCKS lässt sich eine Proxy-Firewall einrichten, die bei vertrauten SOCKS-Servern eine anonyme Bewegung im Internet erlauben. SOCKS-Dienste erfordern Einstellungen an der Software.





Copyright (c) Galileo Press GmbH 2004
Für Ihren privaten Gebrauch dürfen Sie die Online-Version natürlich ausdrucken. Ansonsten unterliegt das <openbook> denselben Bestimmungen, wie die gebundene Ausgabe: Das Werk einschließlich aller seiner Teile ist urheberrechtlich geschützt. Alle Rechte vorbehalten einschließlich der Vervielfältigung, Übersetzung, Mikroverfilmung sowie Einspeicherung und Verarbeitung in elektronischen Systemen.


[Galileo Computing]

Galileo Press GmbH, Gartenstraße 24, 53229 Bonn, Tel.: 0228.42150.0, Fax 0228.42150.77, info@galileo-press.de