Weitere aktuelle Java-Titel finden Sie bei dpunkt.
 Inhaltsverzeichnis   Auf Ebene Zurück   Seite Zurück   Seite Vor   Auf Ebene Vor   Eine Ebene höher   Index


13.2.2

Streamsockets


Mit Streamsockets wird eine feste Verbindung zu einem anderen Rechner aufgebaut, die für die Dauer einer Übertragung bestehen bleibt. Dies macht nur Sinn, wenn die Verbindung zum anderen Rechner relativ gut ist und auch für die gesamte Dauer der Übertragung aufrechterhalten werden kann. Die Datenpakete, die so übertragen werden, kommen in der Reihenfolge am Zielrechner an, in der sie abgeschickt wurden. Geht ein Datenpaket auf der Reise zum Zielrechner verloren, wird es automatisch nachgeschickt. Auf diese Weise ist gewährleistet, dass auch alle Pakete in der richtigen Reihenfolge am Ziel ankommen.

Allgemein beinhaltet die Socket-Programmierung folgende Schritte: Wie der genaue Ablauf in der Praxis ist, wird am Beispiel des Echo-Servers erörtert. Der Echo-Server ist ein Server, der bei UNIX-Rechnern an Port 7 zu finden ist und dessen Funktion darin besteht, alle Zeichen, die er zugeschickt bekommt, wieder zum Absender zurückzuschicken. Wenn man einen Rechner besitzt, der keinen Echo-Server hat, kann man den letzten Abschnitt dieses Kapitels vorziehen und den dort beschriebenen Server benutzen.

Eine Verbindung zu diesem Server kann mit
  server = new Socket(getCodeBase().getHost(), 7);
aufgebaut werden. server ist hierbei eine Objektvariable des Typs Socket. Dem Konstruktor von Socket wird der Host-Name und die Port-Nummer des Rechners übergeben, zu dem der Kontakt hergestellt werden soll. Statt des Host-Namens kann auch ein Exemplar von InetAddress verwendet werden. Dies findet z. B. Verwendung, wenn man schon ein Exemplar von Socket erzeugt hat und zum gleichen Host eine weitere Verbindung aufbauen will. Von dem schon erzeugten Socket-Exemplar kann man über getInetAddress() die InetAddress abfragen und dem neuen Konstruktor übergeben:
  secondsocket = 
    new Socket(firstsocket.getInetAddress(), 1234);
Eine andere Möglichkeit, die InetAddress eines Hosts zu ermitteln, ist, die statische Methode getByName(String) der Klasse InetAddress mit dem Host-Namen als Argument aufzurufen.

Wenn kein Socket erzeugt werden kann, können folgende Exceptions ausgelöst werden: Um über den angelegten Socket mit dem anderen Rechner zu kommunizieren, benötigt man Methoden, über die die Ein- und Ausgabe abgewickelt werden können:

Die Klasse Socket besitzt die Methoden getInputStream() und getOutputStream(), die als Ergebnis die entsprechenden Ein- und Ausgabe-Streams liefern.

Soll die Verbindung wieder abgebaut werden, muss man die Methode close() des Sockets aufrufen:
  server.close();
Auch bei dieser Methode kann eine IOException ausgelöst werden.

Nach dieser kleinen Einführung in die Stream-Sockets in Java nun zurück zum Echo-Server:

Das Applet besitzt ein TextField outgoing und eine TextArea incoming. Die Daten, die man in das TextField eingibt, werden an den Echo-Server geschickt und von diesem zurückgesendet.

Ist der Text wieder beim Applet angekommen, wird er in der TextArea angezeigt. Die Initialisierung der Verbindung wird in der init()-Methode vorgenommen:
  try {
    // Aufbau der Verbindung
    server = new Socket(getCodeBase().getHost(), 7);
    // Anlegen von Ein- und Ausgabestream
    in = new DataInputStream(server.getInputStream());
    out = new DataOutputStream(server.getOutputStream());
    outgoing.addActionListener(this);
  }
  catch(Exception e) {
    ok = false;
    incoming.append(e.getMessage()+"\n");
    outgoing.setEditable(false);
  }
Zuerst wird die Verbindung aufgebaut und anschließend die Ein- und Ausgabe-Streams erzeugt. Tritt ein Fehler beim Erzeugen der Streams auf, wird eine Fehlermeldung in der TextArea ausgegeben und die Initialisierung abgebrochen.
  public void close() {
    ok = false;
    // Anhalten des Einlese-Threads
    stop();
    try {
      // Verbindung schließen
      server.close();
    }
    catch(IOException e) {
      incoming.append(e.getMessage()+"\n");
    }
  }

Die boolesche Variable ok zeigt an, ob die Verbindung in Ordnung ist. Tritt ein Fehler auf, erhält ok den Wert false. Das Applet implementiert das Interface Runnable, um zu jedem Zeitpunkt der Programmausführung eintreffenden Text verarbeiten zu können:
  public void run() {
    // Überprüfen, ob die Initialisierung der
    // Verbindung geglückt ist
    if (ok) {
      try {
        // Einlesen von Zeichen, bis zum EOF-Character
        // und anschließende Ausgabe
        String text;
        while((text = in.readUTF()) != null) {
          incoming.append(text+"\n");
        }
      }
      // Im Fehlerfall Ausgabe einer Meldung und
      // Schließen der Verbindung
      catch (IOException e) {
        incoming.append(e.getMessage()+"\n");
      }
      close();
    }
  }

In der run()-Methode wird zeilenweise Text aus dem Stream, der mit dem Server verbunden ist, gelesen und in der TextArea angezeigt. Dieser Vorgang wird so lange ausgeführt, bis ein EOF-Character eingelesen wird. Das ist zum Beispiel der Fall, wenn man den Server-Prozess beendet.

Nun ist bekannt, wie Text ausgegeben wird. Aber auf welche Weise erfolgt die Eingabe? Hierzu wird die actionPerformed(ActionEvent)-Methode des Beispiels betrachtet:
  public void actionPerformed(ActionEvent e) {
    // Wurde Text in das Textfeld eingegeben?
    if (e.getSource() instanceof TextField) {
      // Schicke Meldung an Echo-Server
      try {
	      out.writeUTF(outgoing.getText());
	      outgoing.setText("");
      }
      catch(IOException ex) {
	      incoming.append(ex.getMessage()+"\n");
      }
    }
  }

Wurde eine Eingabe im TextField mit Return abgeschlossen, so wird die Methode actionPerformed() aufgerufen (da der Client das Interface ActionListener implementiert und sich auch zuvor beim Textfeld als Action-Listener registriert hat). Dort wird zuerst einmal überprüft, ob das Ereignis dem TextField entstammt. Ist dies der Fall, wird der Text, den das TextField beinhaltet, einfach in den Ausgabe-Stream des Sockets geschrieben, wenn die Verbindung zum Echo-Server noch besteht. Danach wird der Text des TextFields gelöscht. Die Lese- und Schreiboperationen werden in diesem Beispiel mit den Methoden readUTF() bzw. writeUTF() durchgeführt. Diese Methoden sind in den Data-Streams enthalten und erlauben das Lesen bzw. Schreiben von Unicode-Zeichen im UTF-8-Format . Hierdurch ist es auf recht einfache Weise möglich, Text ohne Zeichenkonvertierung zwischen Rechnern auszutauschen. Würde man die Lese- und Schreiboperationen mit den Reader- bzw. Writer-Klassen durchführen, wäre bei jeder Operation eine Konvertierung zwischen Unicode und dem Zeichensatz auf der Zielplattform erforderlich.

Wenn mit showDocument() eine URL innerhalb eines Browsers angezeigt werden soll, kann es vorkommen, dass sich dieses Dokument schon innerhalb des Browser-Caches befindet. Erfolgt der Aufruf, wird natürlich die Version des Dokuments geladen, die sich momentan im Cache befindet. Ist es erforderlich, ein Dokument explizit neu zu laden, so kann dies auch mit Hilfe von Sockets geschehen.

Material zum Beispiel


 Inhaltsverzeichnis   Auf Ebene Zurück   Seite Zurück   Seite Vor   Auf Ebene Vor   Eine Ebene höher   Index

Copyright © 2002 dpunkt.Verlag, Heidelberg. Alle Rechte vorbehalten.