Galileo Computing < openbook > Galileo Computing - Professionelle Bücher. Auch für Einsteiger.
Professionelle Bücher. Auch für Einsteiger

Java ist auch eine Insel von Christian Ullenboom
Programmieren für die Java 2-Plattform in der Version 5 (Tiger-Release)
Buch: Java ist auch eine Insel
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 Bilder-Handler
    gp 16.3.4 Zusammenfassung Content- und Protokoll-Handler
    gp 16.3.5 Im Detail: vom URL zu URLConnection
    gp 16.3.6 Der Protokoll-Handler für Jar-Dateien
    gp 16.3.7 Autorisierte URL-Verbindungen mit Basic Authentication
  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 Lebt der Rechner?
    gp 16.5.2 Das Netz ist Klasse …
    gp 16.5.3 IP-Adresse des lokalen Hosts
    gp 16.5.4 Die Methode getAllByName()
  gp 16.6 NetworkInterface
  gp 16.7 Mit dem Socket zum Server
    gp 16.7.1 Das Netzwerk ist der Computer
    gp 16.7.2 Standarddienste unter Windows nachinstallieren
    gp 16.7.3 Eine Verbindung zum Server aufbauen
    gp 16.7.4 Server unter Spannung: die Ströme
    gp 16.7.5 Die Verbindung wieder abbauen
    gp 16.7.6 Ein kleines Echo – lebt der Rechner noch?
    gp 16.7.7 Blockierendes Lesen
    gp 16.7.8 Informationen über den Socket
    gp 16.7.9 Mit telnet an den Ports horchen
  gp 16.8 Client/Server-Kommunikation
    gp 16.8.1 Warten auf Verbindungen
    gp 16.8.2 Ein Multiplikations-Server
    gp 16.8.3 Von außen erreichbar sein
  gp 16.9 SLL-Verbindungen mit JSSE
  gp 16.10 Apache Jakarta Commons HttpClient und Net
    gp 16.10.1 Jakarta Commons HttpClient
    gp 16.10.2 Jakarta Commons Net
  gp 16.11 E-Mail
    gp 16.11.1 Wie eine E-Mail um die Welt geht
    gp 16.11.2 Das Simple Mail Transfer Protocol und RFC 822
    gp 16.11.3 POP (Post Office Protocol)
    gp 16.11.4 E-Mails versenden mit der JavaMail API von SUN
    gp 16.11.5 MimeMultipart-Nachrichten schicken
    gp 16.11.6 E-Mails mittels POP3 abrufen
    gp 16.11.7 Ereignisse und Suchen
  gp 16.12 Arbeitsweise eines Web-Servers
    gp 16.12.1 Das Hypertext Transfer Protocol (HTTP)
    gp 16.12.2 Anfragen an den Server
    gp 16.12.3 Die Antworten vom Server
  gp 16.13 Datagram-Sockets
    gp 16.13.1 Die Klasse DatagramSocket
    gp 16.13.2 Datagramme und die Klasse DatagramPacket
    gp 16.13.3 Auf ein hereinkommendes Paket warten
    gp 16.13.4 Ein Paket zum Senden vorbereiten
    gp 16.13.5 Methoden der Klasse DatagramPacket
    gp 16.13.6 Das Paket senden
    gp 16.13.7 Die Zeitdienste und ein eigener Server und Client
  gp 16.14 Tiefer liegende Netzwerkeigenschaften
    gp 16.14.1 Internet Control Message Protocol (ICMP)
    gp 16.14.2 MAC-Adresse
  gp 16.15 Multicast-Kommunikation


Galileo Computing

16.2 URL-Verbindungen und URL-Objektdowntop

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:


Tabelle 16.1   Schemas nach dem RFC 1738

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

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 URL  downtop

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. Eine Alternative zur oben genannten Form ist

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

Das zweite Argument in diesem Konstruktor ist die Basisadresse der URL und das dritte Argument ist der Name der Ressource relativ zur Basisadresse. Ist diese Basisadresse null, was möglich ist, dann ist die zweite Angabe absolut zu nehmen. Und ist das zweite Argument in absoluter Notation formuliert, wird alles im ersten String 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 Argument.


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" );

Ausnahmen bei der URL-Erzeugung

Jeder der Konstruktoren wirft eine MalformedURLException, wenn das Argument 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.

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



final class java.net.  URL  
implements Serializable

gp  URL( String spec ) throws MalformedURLException
Erzeugt ein Objekt aus der URL-Zeichenkette.
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( URL context, String spec ) throws MalformedURLException
Erzeugt relativ zur gegebenen URL ein neues URL-Objekt.

Galileo Computing

16.2.2 Informationen über eine URL  downtop

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.



final class java.net.  URL  
implements Serializable

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. Im Fall einer IP-Adresse ist die Angabe in [ eckigen Klammern ].
gp  int getPort()
Liefert die Port-Nummer. Ist sie nicht gesetzt, liefert getPort() eine –1.
gp  String getRef()
Gibt den Anker – alles hinter dem # – zurück. Bei »vorne#anker« liefert getRef() den String »anker«.
gp  String getPath()
Gibt nur den Pfad der URL ohne Anker zurück.
gp  String getFile()
Gibt den Dateinamen der URL zusammen mit dem Anker zurück.
gp  String getQuery()
Liefert den Anfragestring, also das, was hinter dem Fragezeichen steht.
gp 

Das kleine nachfolgende Programm erzeugt ein URL-Objekt für http://java-tutor.com:80/faq.html#lang?key=val und gibt die ermittelbaren Attribute aus.

Listing 16.1   ParseURL.java


import java.net.MalformedURLException;
import java.net.URL;

class ParseURL
{
  public static void main( String args[] )
{
    try
    {
      URL url = new URL( 
"http://java-tutor.de:80/java/faq/index.html?key=val#Lang"; );

      System.out.println( url.getProtocol() );  // http
      System.out.println( url.getHost() );      // java-tutor.com
      System.out.println( url.getPort() );      // 80
      System.out.println( url.getFile() );      // /java/faq/index.html?key=val
      System.out.println( url.getPath() );      // /java/faq/index.html
      System.out.println( url.getQuery() );     // key=val
      System.out.println( url.getRef() );       // Lang
    }
    catch ( MalformedURLException e )
    {
      System.out.println( "MalformedURLException: " + e );
    }
  }
}

Verweisen zwei 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

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.

Galileo Computing

16.2.3 Der Zugriff auf die Daten über die Klasse URL  downtop

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

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.2   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 wird erwähnt, dass diese Funktion eigentlich nur eine Abkürzung für openConnection().getInputStream() ist. openConnection() erzeugt ein URLConnection-Objekt und auf diesem liefert getInputStream() den Eingabestrom. 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-Server  toptop

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.5.0/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 SOCKS 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 © 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