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.9 Client/Server-Kommunikationdowntop

Bevor wir nun weitere Dienste untersuchen, wollen wir einen kleinen Server programmieren. Server bauen keine eigene Verbindung auf, sondern horchen an ihrem zugewiesenen Port auf Eingaben und Anfragen. Ein Server wird durch die Klasse ServerSocket repräsentiert. Da wir einen Server selber programmieren wollen, erzeugen wir ein ServerSocket-Objekt mit einem Konstruktor, dem wir einen Port als Parameter übergeben.


Beispiel Wir richten einen Server ein, der am Port 1234 horcht.
ServerSocket serverSocket = new ServerSocket( 1234 );

Natürlich müssen wir unserem Client eine noch nicht zugewiesene Port-Adresse zuteilen, andernfalls ist uns eine IOException sicher. Das häufig verwendete 1234 ist zwar schon vom Infoseek Search Agent (search-agent) zugewiesen, sollte aber dennoch nicht zu Problemen führen, da er auf dem eigenen Rechner gewöhnlich nicht installiert ist. Bei Unix-Systemen können nur Root-Besitzer Ports unter 1024 nutzen. Unter dem herkömmlichen Windows ist das egal.

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


Galileo Computing

16.9.1 Warten auf Verbindungendowntop

Nachdem der Socket eingerichtet ist, kann er auf hereinkommende Meldungen reagieren. Mit der blockierenden Methode accept() der ServerSocket-Klasse nehmen wir genau eine wartende Verbindung an:

Socket server = serverSocket.accept();

Nun können wir mit dem zurückgegebenen Client-Socket genau so verfahren wie mit dem schon programmierten Client. Das heißt, wir öffnen Ein- und Ausgabekanäle und kommunizieren. In der Regel wird ein Thread den Client-Socket annehmen, damit der Server schnell wieder verfügbar ist und neue Verbindungen annehmen und verarbeiten kann.

Wichtig bleibt zu bemerken, dass die Konversation nicht über den Server-Socket selbst läuft. Dieser ist immer noch aktiv und horcht auf eingehende Anfragen. Die accept()-Methode sitzt daher oft in einer Endlosschleife und erzeugt für jeden Hörer einen Thread. Die Schritte, die also jeder Server vollzieht, sind folgende:

1. Einen Server-Socket erzeugen, der horcht
2. Mit der accept()-Methode auf neue Verbindungen warten 3. Ein- und Ausgabestrom vom zurückgegebenen Socket erzeugen 4. Mit einem definierten Protokoll die Konversation unterhalten 5. Stream von Client und Socket schließen 6. Bei Schritt 2 weitermachen oder Server-Socket schließen

Der Server wartet auch nicht ewig

Soll der Server nur eine gewisse Zeit auf einkommende Nachrichten warten, so lässt sich ein Timeout einstellen. Dazu ist der Methode setSoTimeout() die Anzahl der Millisekunden zu übergeben. Nimmt der Server dann keine Fragen entgegen, bricht die Verarbeitung mit einer InterruptedIOException ab. Diese Exception gilt für alle Ein- und Ausgabe-Operationen und ist daher auch eine Ausnahme, die nicht im Net-Paket, sondern im IO-Paket deklariert ist.


Beispiel Der Server soll höchstens eine Minute auf eingehende Verbindungen warten.
ServerSocket server = new ServerSocket( port );
// Timeout nach 1 Minute
server.setSoTimeout( 60000 );
try {
  Socket socket = server.accept();
} catch ( InterruptedIOException e ) {
  System.err.println( "Timeout after one minute" );
}


Galileo Computing

16.9.2 Ein Multiplikations-Servertoptop

Der erste Server, den wir programmieren wollen, soll zwei Zahlen multiplizieren. Dazu reichen wir ihm im Eingabestrom zwei Zahlen, die er dann multipliziert und zurückschreibt.

Listing 16.13 MulServer.java

import java.net.*;
import java.io.*;
class MulServer
{
  public static void main( String args[] ) throws IOException
  {
    Socket client = server.accept();
    InputStream in = client.getInputStream();
    OutputStream out = client.getOutputStream();
    int start = in.read();
    int end = in.read();
    int result = start * end;
    out.write( result );
  }
}

Wir starten den Server auf Port 3141. Nun geht es auf der anderen Seite mit dem Client weiter:

Listing 16.14 MulClient.java

import java.net.*;
import java.io.*;
class MulClient
{
  public static void main( String args[] ) throws IOException
  {
    Socket server = new Socket ( "localhost", 3141 );
    
    InputStream in = server.getInputStream();
    OutputStream out = server.getOutputStream();
    
    out.write( 4 );
    out.write( 9 );
    
    int result = in.read();
    System.out.println( result );
    
    server.close();
  }
}

Natürlich ist der Server in der Funktionalität beschränkt, da nur Bytes übertragen werden. So kann das Ergebnis nicht größer als 127 werden, denn ansonsten würde es falsch übermittelt. Dennoch lässt sich das Programm leicht als Ausgangspunkt für einige Server erweitern.

Ein anderer Punkt ist, dass Server im Allgemeinen multithreaded ausgelegt sind, damit sie mehrere Anfragen gleichzeitig ausführen können. Noch besser ist, die Threads in einen Thread-Pool zu legen, denn ein neuer Thread pro Anfrager ist eine teure Tat.


Hinweis In einer realistischen Client/Server-Anwendung mit Sockets würden immer gepufferte Ströme eingesetzt, um nicht laufend kleine Datenpakete zu senden. Werden jedoch Ströme wie BufferedInputStream oder BufferedOutputStream eingesetzt, so sollte bedacht werden, dass die Informationen im Puffer zwischengespeichert werden und dadurch nicht direkt zum anderen Rechner übertragen werden. In einer Kommunikation Anfrage/Ergebnis muss die Anfrage übertragen werden und darf nicht im Puffer verweilen. Daher muss bedacht werden, zu passenden Zeitpunkten mit den flush()-Methoden der Puffer-Klassen die aufgenommenen Daten auch zu übertragen, damit die Kommunikation weitergehen kann.





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