Client/Server-Anwendungen mit TCP

Werbung
4
Client/Server-Anwendungen mit TCP
Das wichtigste Protokoll der Transportschicht im TCP/IP-Schichtenmodell ist das Transmission Control Protocol (TCP). Es ist
aufwändiger als DDP. stellt aber dafür eine verlässliche Verbindung zwischen Client und Server her. Viele bekannte IntemetDienste wie PTP (File Transfer Protocol), Telnet, SMTP
(SimpleMail Transfer Protocol), POP (Post Office Protocol) und
HTTP(Hypertext Transfer Protocol) nutzen TCP.
4.1
Das Protokoll TCP
TCP ist im Gegensatz zu UDP ein verbindungsorientiertes Protokoll. Zwischen zwei Prozessen (Client und Server) wird eine
virtuelle Verbindung hergestellt. über die in Form eines bidirektionalen Datenstroms Bytefolgen beliebiger Länge geschickt werden können.
Vor der übertragung von Daten muss der Client eine Verbindung
zum Server anfordern. Wird die Verbindung nicht mehr gebraucht, fordert einer der beteiligten Prozesse TCP auf, sie abzubauen.
~
()
( An f rage
(
Client
1Il
An tw or t ( )
Bidirektionale TCP-Verbindung
~
Server
TCP sorgt für die Aufteilung der Daten in einzelne Pakete. garantiert . dass die Pakete den Empfänger in der richtigen Reihenfolge
erreichen, und initiiert die Neuübertragung von verloren gegangenen oder defekten Paketen.
TCP ist im RFC 793 der IETF spezifiziert (siehe auch http://www .
i etf .org I rfel rfe 793. t xt) ,
Bild 4.2 zeigt den Aufbau eines TCP-Pakets. das Bestandteil der
Nutzdaten des IPv4-Pakets ist.
Der TCP-Kopfteil enthält.
•
die Portnummer des Senders (2 Byte).
•
die Portnummer des Empfängers (2 Byte).
•
weitere Felder mit einer Gesamtlänge von 16 Byte wie z. B.
Reihenfolgenummer und Prüfsumme,
Bild 4.1'
TCP-Verbindung
4
118
•
Client/Server-Anwendungen mit TCP
optionale Felder variabler Länge.
Darauf folgen dann die eigentlichen Nutzdaten des TCP-Pakets.
i
Bild 42.·
Aufbau des TCPPakets
!4
----r---
,I
IP-Adresse des Senders
IPv4-Kopfteil
-+
IP-Adresse des Empfängers
· ..
Portnummer
des Senders
TCP-Kopfteil
--+----
TCP-Nutzdaten
4.2
Client-Socket und
Server-Socket
--r-----
· ..
I
20 Byte
J .
--r----0- 40 Byte
Portnummer
des Empfängers
20 Byte
· ..
optionale Felder
Nutzdaten
TCP-Sockets
1m Gegensatz zu DDP wird bei TCP zwischen Client-Socket und
Server-Socket unterschieden.
Der Server-Socket versetzt den Server in die Lage , die Verbindungsanforderungen der Clients abzuhören und dann mehrere
Clients zu bedienen.
Bevor Daten zwischen Client und Server ausgetauscht werden
können, muss eine Verbindung zwischen Sockets hergestellt
werden. Der Aufruf der Methode accept des Server-Sockets blockiert den Server so lange, bis ein Client versucht, Kontakt aufzunehmen. accept liefert als Ergebnis einen weiteren Socket,
über den dann die eigentliche Kommunikation stattfindet.
Die Kontaktaufnahme auf der Client-Seite geschieht automatisch
bei der Erzeugung des Client-Sockets mit einem geeigneten Konstruktor. Eine Verbindung kann natürlich nur hergestellt werden,
wenn der Server zeitlich vor dem Start des Client accept aufgerufen hat. Über Ein- und Ausgabeströme können nun Daten empfangen bzw. gesendet werden.
Die Verbindung wird abgebaut, wenn einer der beiden Sockets
geschlossen wird.
4.2
TCP-Sockets
Die Klasse java. net Socket implementiert einen Client-Socket.
119
Socket
Socket(InetAddress address. int port) throws IOException
erzeugt einen Client-Socket und bindet ihn an die vorgegebene
IP-Adresse und Portnummer.
Socket(String host. int port)
throws java.net.UnknownHostExceptlon, IOExcept l on
erzeugt einen Client-Socket und bindet ihn an den vorgegebenen
Rechner und an die vorgegebene Portnummer.
void close() throws IOException
schließt den Socket.
boolean isClosed()
liefert t rue, falls der Socket geschlossen ist. sonst fa 1se.
vOld setSoTimeout(lnt tl meout) throws SocketExceptlon
setzt ein Timeout in Millisekunden. read für den Eingabestrom
dieses Sockets blockiert höchstens ti rre out Millisekunden und
löst dann die Ausnahme ja va.net.SocketTlmeoutExceptlon aus.
Wird tlmeout = 0 gesetzt, so wird die Timeout-Steuerung deaktiviert.
int getSoTimeout() throws SocketException
liefert die Timeout-Angabe.
InputStream getlnputStream() throws IOException
liefert einen Eingabestrom für diesen Socket.
Input / Output
OutputStream getOutputStream() throws IOExcept i on
liefert einen Ausgabestrom für diesen Socket.
Wird einer der beiden Datenströme mit der entsprechenden
close-Methode geschlossen. so wird auch der zugehörige Socket
geschlossen.
Mit einer der folgenden Methoden kann jeweils nur einer der
beiden Datenströme (Eingabe bzw. Ausgabe) geschlossen werden. ohne damit den Socket selbst zu schließen,
void shutdownlnput() throws IOExcept i on
setzt den Eingabestrom für diesen Socket auf EOF (End Of File).
void shutdownOutput() throws IOExcept i on
deaktiviert den Ausgabestrom für diesen Socket.
Die Klasse java .net. ServerSocket implementiert einen Seruer- SeroerSocket
Socket.
ServerSocket(int port) throws IOExcept i on
erzeugt einen Server-Socket und bindet ihn an die vorgegebene
Portnummer.
void close() throws IOExcept i on
schließt den Server-Socket,
4
120
Client/Server-Anwendungen mit TCP
boolean isClosed()
liefert true, falls der Server-Socket geschlossen ist, sonst f al se.
Socket accept() throws IOException
nimmt einen Verbindungswunsch an und erzeugt einen neuen
Socket für diese Verbindung, accept blockiert so lange, bis eine
neue Verbindung aufgenommen "WUrde.
vOld setSoTimeout(int timeout) throws SocketException
setzt ein Timeout in Millisekunden. accept blockiert höchstens
timeout Millisekunden und löst dann die Ausnahme java. net.
SocketTimeoutException aus. Wird timeout = 0 gesetzt, so wird
die Timeout-Steuerung deaktiviert.
int getSoTimeout() throws IOException
liefert die Timeout-Angabe.
backlag
Verbindungswünsche. für die mittels accept noch kein Socket
erzeugt werden konnte, werden in einer Warteschlange
(backlag) verwaltet Falls die Warteschlange voll ist und ein neuer Verbindungswunsch eingeht, wird beim Client die Ausnahme
java. net. ConnectExcepti on ausgelöst. Die maximale Länge der
Warteschlange ist standardmäßig 50,
Mit dem Konstruktor
ServerSocket(int port, int backlog) throws IOException
kann die maximale Länge der Warteschlange explizit festgelegt
werden.
AblauJschritte
1. Der Server erzeugt ein ServerSocket-Objekt, das an einen
vorgegebenen Port gebunden wird,
2, Der Server ruft die Methode accept des Server-Sockets auf
und wartet auf den Verbindungswunsch eines Client.
3, Der Client erzeugt ein Socket-Objekt mit der Adresse und der
Portnummer des Servers und versucht damit, eine Verbindung zum Server aufzunehmen.
4. Der Server erzeugt ein Socket-Objekt, das das serverseitige
Ende der Kommunikationsverbindung darstellt Die Methode
accept gibt dieses Objekt zurück
5, Um eine bidirektionale Verbindung zwischen Client und Server aufzubauen, stellen Client und Server jeweils einen Ausund Eingabestrom mit Hilfe ihrer Socket-Objekte bereit
6. Nun können Daten mittels Lese- und Schreiboperationen hinund hergeschickt werden,
4.3
Ein Echo-Server und -Client
121
Bild 4.3 zeigt den Ablauf beim Aufbau einer TCP-Verbindung.
Bild 43·
Aufbau einer TCPVerbindung
ServerSocket
I
accept()
<E-
.:~~i2!.
~
!
getlnputStream()
I
clienlSockel
!,
------------------+----------~
gelOJtputStream() !
I
getlnputStream()
getOJtputStream()
-----------------+---------1
Die beiden Sockets sind miteinander verbunden.
Weitere Socket-Methoden sind,
InetAddr es s getLocalAddress()
liefert die lokale IP-Adresse, an die der Socket gebunden ist.
SocketInformationen
int getLocalPort()
liefert die lokale Portnummer, an die der Socket gebunden ist.
Inet Addre s s getInetAddress()
liefert die entfernte IP-Adresse, mit der der Socket verbunden ist.
i nt getPort()
liefert die entfernte Portnummer, mit der der Socket verbunden
ist.
4.3
Ein Echo-Server und -Client
Wir stellen nun eine verbindungsorientierte Version des EchoDienstes vor.
Der Client schickt in einer Schleife jeweils eine Textzeile zum Programm 41
Server, der diese dann wieder an den Client zurückschickt. Die
Eingabe erfolgt über Tastatur.
Hier ist zunächst der Client:
4
122
EchoClient
Client/Server-Anwendungen mit TCP
Die eigentliche Verarbeitung findet in einer try-Anweisung statt.
Die Verbindung zum Server wird am Ende mit socket.close()
abgebaut.
lmport
lmport
lmport
lmport
lmport
java
java
java
java
java
lo.BufferedReader;
lo.IOExceptlon;
lo.InputStreamReader;
lo.PrlntWrlter;
net.Socket;
public class EchoClient (
public static voi d main(String[] args) {
Socket socket ~ null;
try (
String host ~ args[O];
int port ~ Integer.parselnt(args[ l]);
socket ~ new Socket(host. port);
BufferedReader in ~ new BufferedReader(
new InputStreamReader(socket.getlnputStream()));
PrlntWrlter out = new PrlntWrlter(socket
.getOutputStream(). true);
BufferedReader input ~ new BufferedReader(
new InputStreamReader(System. in));
String msg ~ in.readLine();
System.out.println(msg);
Strlng I i ne :
whi le (true) (
line ~ input.readLine();
if (line ~~ null 11 line.equals("q"))
break;
out.println(line);
System.out.println(in readLine());
}
in.close();
out.close() ;
input.close() ;
catch (Exception e) (
System.err.println(e);
finally {
try {
i f (socket !~ null)
socket.close();
catch (IOException e)
4.3
Ein Echo-Server und -Client
123
Zunächst wird ein Client-Socket erzeugt und damit versucht, die
Verbindung zum Server herzustellen. Das BufferedReader-Objekt
in ist der Eingabestrom. das Pri nt Wri ter-Objekt out der Ausgabestrom für diesen Socket.
Dann wird eine Nachricht des Servers ausgegeben. In einer
Schleife werden nun von der Tastatur Textzeilen eingelesen, in
den Ausgabestrom O ll t geschrieben und damit zum Server geschickt. in. readLi ne () blockiert so lange. bis die Antwort vom
Server vorliegt. Diese wird dann angezeigt.
Wir zeigen zwei Server-Versionen. Die erste Version kann nicht
mehrere Clients gleichzeitig bedienen. Werden z.B. zwei Clients
kurz hintereinander gestartet, so werden sie der Reihe nach bedient. Erst wenn der erste Client beendet "WUrde, kommt der
zweite zum Zuge. Es handelt sich also um einen so genannten
iterativen Server.
lmport
lmport
lmport
lmport
lmport
lmport
lmport
java. ia Buf feredReader ;
java. io. IOExceptlon;
j ava. r o . InputSt reamReader ;
java. lo.PrlntWriter;
java net. InetAddress;
java net.Ser verSocket;
java net.Socke t;
publlc class EchoSer verl
prl vate lnt port;
private int backlog;
public EchoSer ver l( i nt port. i nt backlog) (
this.port ~ port;
this.backlog ~ backlog ;
public void startServer() {
t ry (
ServerSocket server
new ServerSocket (port, backlog);
=
Inet Addr es s addr ~ Inet Address .get Local Hos t( );
System.out.println (" EchoServer 1 auf"
+ addr. get HostNarre () + "/" + addr. getHostAddress ()
+ ".;" +
port
+ "
gestartet ... ");
process(server);
catch (IOException e)
System.err.println (e);
EchoServerl
4
124
Client/Server-Anwendungen mit TCP
prlvate vOld process(ServerSocket server)
throws IOException {
whi le (true) (
Socket cllent
=
server.accept();
String clientAddr ~ client.getInetAddress()
.getHostAddress();
int clientPort ~ client.getPort();
System.out.println("Verbindung zu " + clientAddr
+ ".;"
+ cllentPort + " aufgebaut");
try (
BufferedReader in ~ new BufferedReader(
new InputStreamReader(client.getInputStream()));
PrlntWrlter out = new PrlntWrlter(cllent
.getOutputStream(). t r ue ) ;
out pr i nt ln ("Server l st berelt .. . ");
String input;
while ((input ~ in.readLine())
out. pri nt 1n(i nput);
!~
null) {
in.close();
out.close() ;
catch (IOException e)
System.err.println(e);
finally {
try {
if (client !~ null)
cl i ent. cl ose();
catch (IOException e)
System.out.println("Verbindung zu "
+ cl i entPort + " abgebaut");
+
clientAddr
+ ";"
public static void main(String [] args) {
if (args.length !~ 1 && args.length !~ 2)
System.err
.println("java EchoServer1 <port> [<backlog>]");
System.exit(l);
int port ~ Integer parse Int(args[O]);
int backlog ~ 50;
4.3
Ein Echo-Server und -Client
125
i f (args.length ~~ 2 )
backlog ~ Int eger . pars el nt (args [l ] ) :
new EchoSer ver 1(port . backlog).startServer():
Der Server-Socket wird erzeugt und an die vorgegebene Portnummer des lokalen Rechners gebunden. Hier wird auch die
maximale Länge der Warteschlange (backlag) vorgegeben. Somit
kann das Verhalten der Anwendung mit unterschiedlichen Werten getestet werden.
Je Schleifendurchgang in der Methode process erfolgen diese
Schritte;
•
Aufruf der Methode accept, die so lange blockiert, bis ein
Client versucht, Verbindung aufzunehmen.
•
Bereitstellung der Ein- und Ausgabesträme 'in und out.
•
Sendung einer Nachricht an den Client.
•
In einer Schleife werden Textzeilen eingelesen und sofort in
den Ausgabestrom geschrieben. Die Schleife läuft so lange,
bis der Client die Verbindung und damit seinen Ausgabestrom geschlossen hat.
•
Der Socket wird geschlossen.
Aufruf des Servers;
Test
start ja va -cp bin EchoSer ver1 50000
Aufruf des Client:
java -cp bin EchoCl i ent loca lhost 50000
Der Server meldet den Auf- und Abbau der Verbindung;
EchoSer ver1 auf abts-PC/ 192. 168.2 . 100:50000 gestarte t
Verbindung zu 127. 0. 0.1 :49333 aufgebaut
Verbindung zu 127. 0. 0.1 :49333 abgebaut
Der Server kann über Tastatur mit Strg+C abgebrochen werden.
Die zweite Version des Echo-Servers ermöglicht die parallele EchoServer2
Bedienung mehrerer Clients. Dazu erfolgt die Kommunikation
mit einem Client innerhalb eines Threads.
4
126
lmport
lmport
lmport
lmport
import
import
import
ja va
ja va
ja va
ja va
ja va
ja va
ja va
Client/Server-Anwendungen mit TCP
lo.BufferedReader;
lo. IO Exceptlon;
lo. InputStreamReader;
lo.PrintWriter;
net. InetAddress;
net.ServerSocket;
net.Socket;
public class EchoServer2
private i nt port;
publ i c EchoSer ver2(int port) (
this port ~ port;
public void startServer() {
try (
ServerSocket server = new ServerSocket(port);
Inet Address addr ~ Inet Address .get Local Host ( ) ;
System.out.println("EchoServer2 auf"
+ addr. getHostName() + "/" + addr. getHostAddress()
+ '";" + port + " gestartet ... ");
whi le (true) (
Socket client = server.accept();
new EchoThread(cl i ent ) .st art ( ) ;
}
catch ( IOException e)
System.err.pri ntln(e);
priv at e class EchoThread extends Thread (
pri vate Socket cli ent;
public EchoThread(Socket client) (
this client = client;
public voi d run() (
String clientAddr ~ client get lnetAddress()
.getHostAddress();
int clientPort ~ client.getPort();
System.out.println("Verbindung zu "
+
clientAddr
+ ".;"
+ clientPort + " aufgebaut");
try (
BufferedReader in ~ new Buf feredReader (
new Input St reamReader (cl i ent .getl nput St ream( ) ) ) ;
4.3
Ein Echo-Server und -Client
PrlntWrlter out
=
127
new PrlntWrlter(cllent
.getOutputStream(). true);
out pr i nt l n( "Ser ver i s t berel t ... ");
String input;
while ((input ~ in.readLine())
out.println(input);
!~
null) (
in.close() ;
out.close() ;
catch (IO Exception e)
System.err.println(e);
finally {
try {
if (client !~ null)
cl ien t.close();
catch (IOExcep tion e )
System.out.println("Verbindung zu " + clientAddr + '";"
+ clientPort + " abgebaut");
public static voi d main(String[] args)
int port ~ Int eger .par sel nt (ar gs [ O] ) ;
new Ec hoSe r ver 2(port ) . st ar t Ser ve r ( ) ;
Sobald die Methode accept einen Verbindungswunsch angenommen hat, wird mit Hilfe des gelieferten Sockets ein
EchoThread-Ob je kt erzeugt und dieser Thread dann gestartet.
Sofort ist der Server wieder bereit, eine neue Verbindung aufzunehmen. Pro Verbindung existiert also ein eigener Thread, der
den entsprechenden Client bedient.
4
128
4.4
Programm 4.2
Client/Server-Anwendungen mit TCP
Ein Download-Programm
Client und Server des folgenden Beispiels ermöglichen das Herunterladen von beliebigen Dateien aus einem vorgegebenen
Verzeichnis.
Bild 4.4.·
Download
Kommandos:
li s t
g et <f i le>
q
Download
FileServer
FileClient
EBJ
Folgende Kommandos kann der Client verwenden:
FileSeroer
lis t
Ausgabe aller Dateinamen des Serververzeichnisses
get <fi l e>
Download der Datei <fl le>
q
Beenden des Programms
lmport
lmport
lmport
lmport
lmport
lmport
lmport
lmport
l mport
i mport
l mport
lmport
lmport
ja va
ja va
ja va
ja va
ja va
ja va
ja va
ja va
java
java
java
ja va
ja va
lo.Buffered InputStream;
lo. ByteArrayOutputStream;
lo. EO FExceptlon;
lO. Flle;
lO.Flle Fllter;
lo. Flle InputStream;
l o.Fl le Not FoundExcept l on;
l o.IOExcept l on;
lO ObjectInputStream;
io.ObjectOutp utStream;
net. InetAddress;
net.ServerSocket;
net.Sock et;
publlC class Fl l eServer
prlvat e lnt port;
prl vate Fl l e dl r;
public Fi l eServer (i nt port. Fi l e dir) {
th is port ~ port;
th is dir ~ dir;
4.4
Ein Download-Programm
129
publie void startServer() {
try (
ServerSocket server = new ServerSocket(port);
InetAddress addr ~ InetAddress.getLoealHost();
System.out.println("FileServer auf"
+ addr. getHostNarre () + "/" + addr. getHostAddress()
+ ".;" + port + " gestartet ... ");
whi 1e (true) (
Socket cllent = server accept();
new FileThread(elient. dir).start();
}
eateh (IOExeeption e)
System.err.println(e);
private elass FileThread extends Thread (
private final statie long MAX_SIZE ~ 1024 * 1024; // 1 MB
private final statie int TIMEOUT ~ 600000; /I 10 Min.
private Socket client;
private File dir;
private ObjectInputStream in;
private ObjeetOutputStream out;
publie FileThread(Soeket elient. File dir) (
this.client = client;
this.dir ~ dir;
publie void run() (
String elientAddr ~ elient.getlnetAddress()
.getHostAddress();
int elientPort ~ elient.getPort();
System.out.println("Verbindung zu " + elientAddr
+ '";"
+ cl i entPort + " aufgebaut");
try (
elient.setSoTirreout(TIMEOUT);
out
=
new ObjectOutputStream(
client.getOutputStream());
out. fl ushr ) ;
in = new ObjectlnputStream(client.getlnputStream());
whi 1e (true) (
String errd ~ (String) in.readObjeet();
if (emd.equals("list")) (
doList();
} else if (errd.startsWith("get ")) {
4
130
Client/Server-Anwendungen mit TCP
String file ~ cmd.substring (4).trim();
doGet(file) ;
else if (cmd.equal s ("q" ) ) (
break;
in.close();
out.close() ;
catch (EOFException e)
catch (Exception e) (
System.err.println(e);
finally {
try {
if (client !~ null)
cl i ent. cl ose();
catch ( IO Exception e )
System.out.println("Verbindung zu "
+ cl i entPort + " abgebaut");
+
clientAddr
+ ".;"
pri vate void doList() throws IOExcept i on (
// Verzelchnlsnamen werden nlcht zurückgegeben
Fi l e[ ] files ~ dir.listFiles(new FileFilter() (
public boolean accept(File pathname) {
i f (pathname. is Fi le())
re turn true;
else
return false;
}
}) ;
out.writeObj ect(files);
out .flush();
pri vate void doGet(String file) throws IOExcept i on (
Fi l e f ~ new File(dir. file) ;
if (!fisFile()) (
Fi l eNot FoundExcept i on e ~ new FileNotFoundException(
fi 1e);
out.writeObject(e);
out. fl ush() ;
return;
}
if (f.length()
Except l on e
>
=
WlX_S IZE) (
new Exceptlon(flle + " i s t zu grass");
out.writeObject(e);
4.4
Ein Download-Programm
out. fl ush();
return;
BufferedlnputStream fin ~ null;
try (
ByteArrayOutputStream bout ~
new ByteArrayOutputStream();
fin ~ new BufferedlnputStream(new FilelnputStream(f));
byte[] b ~ new byte[1024];
lnt c;
while ((c ~ fin.read(b)) !~ -1) (
bout.write(b. O. c i:
}
byte[] bytes ~ bout.toByteArray();
out.writeObject(bytes);
catch (IOException e) (
System.err.println(e);
finally {
try {
if (fin !~ null)
fin.close();
catch (IOException e) (
public static void main(String[] args) {
if (args.length !~ 2) (
System.err.prlntln("java FlleServer <port> <dlr>");
System. exi t (1) ;
int port ~ Integer.parselnt(args[O]);
File dir ~ new File(args[l]);
if (!dir.isOirectory()) {
System. err. pri nt ln (args[l] + " ist kei n Verzei chni 5");
System. exi t (1) ;
new FileServer(port. dir).startServer();
Der Server wird mit den Parametern Portnummer und Verzeichnis aufgerufen. Zu Beginn wird geprüft, ob der zweite Parameter
ein gültiges Verzeichnis bezeichnet. Die Kommunikation mit
dem Client findet in einem clientbezogenen Thread statt (run-
131
4
132
Client/Server-Anwendungen mit TCP
Methode). Bleibt der Client 10 Minuten inaktiv. wird die Verbindung abgebrochen.
Über den Eingabestrom in (vom Typ ObjecUnputStream) werden
die Kommandos (Stri ng-Objekte) des Client gelesen. über den
Ausgabestrom out (vom Typ ObjectOutputStream) werden Objekte
unterschiedlicher Typen (Dateilisten, Byte-Arrays mit dem Dateiinhalt, Exceptions) an den Client gesendet.
Achtung
Bei der Verwendung von ObjecUnputStream und ObjectOutputStream für die Kommunikation über Sockets ist Folgendes zu
beachten,
Der ObjectüutputStream-Konstruktor schreibt den so genannten
Serialization Stream Header in den Ausgabestrom.
Der Obj ecUnputStream-Konstruktor blockiert, bis der zugeordnete
Obj ectOutputS tream den Header geschrieben hat. Deshalb sollte
unmittelbar nach dem Aufruf des ObjectOutputStreamKonstruktors der Puffer mittels fl ush () geleert werden.
Diese Verhaltenweise wird im Client- und Serverprogramm berücksichtigt, ansonsten "WÜrden sich die Programme gegenseitig
blockieren.
Zum Kommando II st:
Alle Dateinamen (keine Namen von Unterverzeichnissen) des
Serververzeichnisses werden in den Ausgabestrom als Arra y vom
Typ Fi1e geschrieben.
Zum Kommando get:
Wird die angeforderte Datei im Serververzeichnis nicht gefunden
oder überschreitet die Dateigröße den Maximalwert WlX_S IZE. so
wird ein Objekt vom Typ Exception in den Ausgabestrom geschrieben. Ansonsten wird die Datei in ein Byte-Array übertragen
und dieses dann in den Ausgabestrom geschrieben.
FileClient
lmport
lmport
lmport
lmport
lmport
l mport
l mport
l mport
lmport
lmport
ja va
ja va
ja va
java
java
java
java
java
ja va
ja va
lo. BufferedOutputStream;
lO.BufferedReader;
lo.ByteArray InputStream;
l O.Fl l e ;
l o.Fl l eOut put St ream;
lo. IO Except lon;
lo. InputStreamReader;
lO ObjectInputStream;
lO.Obj ectOutputStream;
net.Sock et;
public class Fi le Cl ie nt
prlvat e Strlng host;
4.4
Ein Download-Programm
prlvate
prlvate
private
private
private
133
lnt port;
File dir;
ObjectInputStream in;
ObjectOutputStream out;
BufferedReader input;
public FileClient(String host. int port. File dir) (
this.host ~ host;
this.port ~ port;
this.dir ~ dir;
public void doWork() {
Socket socket ~ null;
try (
socket ~ new Socket(host. port);
in = new ObjectlnputStream(socket.getlnputStream());
out = new ObjectOutputStream(socket.getOutputStream());
out. fl ushr ) ;
input ~ new BufferedReader(new InputStreamReader(
System. in) ) ;
whi 1e (true) {
System.out.println(
"Kommando eingeben (lis t
get <file>
String cmd ~ input.readLine();
if (cmd ~~ null I1 cmd.equals("q")) (
doOuit() ;
break;
1
)
i f (cmd. equals ("1ist"))
doList();
else if (cmd.startsWith("get ")) (
String file ~ cmd.substring(4).trim();
doGet(fi le);
else
System. out. pri nt 1n("Ungue1ti ger Befehl");
in.close() ;
out.close();
input.close();
catch (Exception e) (
System.err.println(e);
finally {
try {
i f (socket ! ~ null)
socket. cl ose () ;
1
q);");
4
134
Client/Server-Anwendungen mit TCP
catch (IOException e) (
private void doOuit() throws IOException (
out. wri teObject ("q");
private void doList() throws IOException,
ClassNotFoundException {
out, wri teObject ("1i st") ;
Fil e[] files - (Fil e[]) in,readObject();
for (File f files) (
System,out,println(f,getName());
private void doGet(String file) throws IOException,
ClassNotFoundException (
out.writeObject("get " + file);
Object obj - in,readObject();
if (obj instanceof Exception)
System,out,println(obj);
return;
BufferedOutputStream fout - null;
try (
File f - new File(dir, file);
ByteArraylnputStream bin - new ByteArraylnputStream(
(byte[]) obj);
fout - new BufferedOutputStream(
new FileOutputStream(f));
byte[] b - new byte[1024];
l nt c :
while ((c - bin read(b)) !- -1) (
fout,write(b, 0, c) :
}
catch (IOException e)
System,err,println(e);
finally {
try {
if (fout !- null)
fout,close();
catch (IOException e)
4.5
Ein Chat-Programm
System.out.prlntln(flle + " wurde uebertragen");
public static void main(String[J args) (
if (args.length !- 3) (
System.err
.prlntln("java FileClient <hast> <port> <dir>");
System. exi t (1) ;
String host - args[OJ;
int port - Integer.parselnt(args[lJ);
File dir - new File(args[2J);
if (!dir.isOirectory()) (
System. err. pri nt ln (args[2] + " ist kei n Verzei chni 5");
System. exi t (1) ;
new FileClient(host. port. dir).doWork();
Der Client wird mit den Parametern Rechnername, Portnummer
und lokales Verzeichnis aufgerufen. Die übertragenen Dateien
werden in dem vorgegebenen Verzeichnis gespeichert.
Zu Beginn wird geprüft, ob der dritte Parameter ein gültiges
Verzeichnis bezeichnet.
über den Ausgabestrom out werden die Kommandos gesendet.
über den Eingabestrom in werden Objekte gelesen und entsprechend ihrem Typ verarbeitet: Ausgabe einer Liste von Dateinamen, Speicherung des übertragenen Dateiinhalts, Ausgabe der
Fehlermeldungen.
4.5
Ein Chat-Programm
Mit dem Chat-Client ist das so genannte "Chatten" mit mehreren Programm 43
Teilnehmern im Netz möglich. Der Teilnehmer kann sich anund abmelden und Textzeilen an alle anderen aktiven Teilnehmer senden. Der Chat-Server registriert die angemeldeten Teilnehmer und verteilt eingehende Nachrichten an alle registrierten
Teilnehmer. Der Chat-Client wird sowohl als Applikation mit
grafischer Oberfläche als auch als Applet realisiert.
135
4
136
ChatServer
lmport
lmport
lmport
lmport
import
import
import
import
ja va
ja va
ja va
ja va
ja va
ja va
ja va
ja va
Client/Server-Anwendungen mit TCP
lo.BufferedReader;
lo. IO Exceptlon;
lo. InputStreamReader;
lo.PrintWriter;
net. InetAddress;
net.ServerSocket;
net.Socket;
util .Vector;
public class ChatServer (
private Vector<Pri nt Writer> manager
new Vector<PrintWriter>();
priv at e int port ;
public ChatServer(int port) (
this port ~ port;
public void startServer() {
try (
ServerSocket server = new ServerSocket(port) ;
Inet Address addr ~ Inet Address getLocal Host();
System.out.println("ChatServer auf"
+ addr. get HostName() + "/" + addr. getHostAddress()
+ '";" + port + " gestartet ... ");
whi le (true) (
Socket client = ser ver.accept();
new ChatThread(client).start();
}
catch ( IOException e)
System.err.println(e);
privat e class ChatThread extends Thread (
private f i nal static int TIMEO UT ~ 600000; // 10 Min.
private Socket client;
pri vate String name;
pri vate BufferedReader in;
pri vate PrintWriter out;
public ChatThread(Socket client) (
this client = client;
pub lic voi d run() (
String clientAddr ~ client.get lnetAddress()
.getHostAddress();
int clientPort ~ client.getPort();
4.5
Ein ehat-Programm
137
try (
client setSoTi meout(TIMEOUT);
new BufferedReader(new Input St reamReader (cl le nt
.get lnputStream()));
out ~ new PrintWriter(client.getOutputStream(). true);
ln
=
login();
System.out.println("Verbindung zu " + cli entAddr
+ cII entPort + " aufgebaut: " + narre);
Strlng message;
wh il e (( message
~
in readLine())
send~ss age (na rre
!~
+ '"."
null)
+ ". " + rress age) ;
in .close() ;
out.close();
catch (IOException e)
System.err.println(e);
finally (
logout() ;
try {
if (cl ient !~ null)
cl ient.close();
) catch (IOException e)
)
System.out.println("Verbindung zu " + cli entAddr
+ cll entPort + " abgebaut: " + name);
pri vate voi d login() throws IOExcepti on (
manager.add (out);
name = ln.readLlne();
send ~ssag e(narre + "
i st dazugekomrren");
private voi d logout() (
manager.remove(out);
sendr-12ssage(narre + " hat s ich verabschledet") ;
prlvate vOld sendMessage(Strlng message)
synchronized (manager) {
for (PrintWriter out manager) (
out.println( message);
+ '"."
4
138
Client/Server-Anwendungen mit TCP
public static voi d main(String[] args)
i nt port ~ Int eger . pars el nt (args [ O] ) ;
new ChatServer(port).startServer();
Das Programmgerüst entspricht den in den letzten Abschnitten
behandelten Beispielen. Kernstück des Programms ist die runMethode.
Der Server verwaltet ein Vector-Objekt manager. Mit Hilfe dieses
Objekts "merkt" sich der Server, wer sich als Teilnehmer angemeldet hat. Nach Aufnahme der Verbindung wird die Referenz
out auf den Ausgabestrom für diesen Teilnehmer im Vektor gespeichert (siehe Methode 1ogi n) und am Ende aus dem Vektor
wieder entfernt (siehe Methode logout).
Die erste Textzeile, die der Server über tn erhält, ist der LoginName des Teilnehmers. Alle anderen empfangenen Zeilen sind
Nachrichten des Teilnehmers an alle aktiven Teilnehmer. Alle
Nachrichten werden mit dem Teilnehmernamen versehen und
mit der Methode sendMessage an alle angemeldeten Teilnehmer
gesendet (siehe Bild 4.5). Mit derselben Methode werden die
Teilnehmer darüber informiert, wer sich an- oder abgemeldet
hat.
Bild4S·
ChatServer
Der Server als
Reflektor
/
/
/
ChatClien t 1
ChatClient
i
,
~
-. -.
ChatClient 2
ChatClient 3
Der Chat-Client ist so programmiert, dass er als Applet oder als
eigenständige Applikation eingesetzt werden kann.
Das Einlesen der Nachrichten vom Server erfolgt in einem eigenen Thread (siehe run-Methode), der in der Methode 1ogi n gestartet wird.
45 Ein ehat-Programm
lmport
lmport
lmport
lmport
lmport
lmport
lmport
lmport
lmport
lmport
lmport
lmport
lmport
lmport
lmport
java. awt. IlJ rder Layout ;
java. awt Contalner;
java. awt. Ev ent Queue ;
java. awt. Fl owLayout;
java. awt. Font ;
java. awt event.Äctlon Event;
java. awt event.ActlonLlstener;
java. awt event. WindowAdapter;
java. awt.e vent. WlndowEvent;
java. io BufferedReader;
java. io BufferedWri ter;
java. to . IOExcept i on;
java. to . Input St reamReader ;
java. lO.OutputStreamWrlter;
java net.Socket;
lmport
lmport
lmport
lmport
lmport
lmport
import
import
javax.swlng
javax.swlng
javax.swlng
javax.swlng
javax.swlng
javax.swing
javax.swing
javax.swing
JApplet;
JButton;
JFrame;
JLabe 1;
JPanel;
JScro11 Pane;
JTextArea;
JTextField;
public class ChatClient extends JApplet implements Runnable.
ActionListener {
pri vate final static int width ~ 500;
pri vate final static int hight ~ 400;
private
pri vate
pri vate
pri vate
JLabel label;
JTextArea area;
JTextField text;
JButton button;
private
pri vate
pri vate
pri vate
pri vate
String hast;
int port;
Socket socket;
BufferedReader in;
BufferedWrlter out;
private volatile Thread t;
prlvate boolean logln = false;
prlvate Strlng name;
// Wird verwendet. um den Client als Applikation zu starten.
prl vate statlc J Frame frame;
// Kommandozellenparameter für dle Socket Inl t l al l sl er ung
private static String[] cmdLineArgs;
139
4
140
Client/Server-Anwendungen mit TCP
11 De r Client kann auch als Applikation gestartet werden.
11 Dies wird ermöglicht. indem ein Frame für das Applet
11 zur Verfügung gestellt wird.
static public void main(String [] args) {
if (args.length !- 2) (
System. err. pr int l n( "java ChatCllent <hast> <port>");
System.exit(l);
11 Wird für Socket Initialisierung benötigt.
cmdLlneArgs = args;
11 Der Cl ient
final ChatClient client - new ChatClient();
frarre - new JFrame("Chat-Client");
fra rre .addWindowListener(new WindowAdapter() (
public void windowClosing(WindowEvent e) (
client.destroy();
System.exit(O);
}
}) ;
client. init();
frarre.getContentPane().add(client);
frarre.setSize(width. hight);
frarre.setVisible(true);
public ChatClient() (
label - new J Label(" ");
JPanel top - new JPanel ();
top. add (1 abe1) ;
area = new JTextArea();
area.setFont(new Font("11mospaced". Font. PLAIN. 14));
area.setLlneWrap(true);
area.setEditable(false);
text - new JTextField(48);
text. setF ont (new Font("11mospaced". Font. PLAI N. 14));
text.setEnabled(false);
text.addActionListener(this);
button - new JButton("Login");
button.setEnabled(false);
button.addActionListener(this);
JPanel input - new JPanel ();
input.setLayout(new FlowLayout(FlowLayout LEFT. 10. 10));
input.add(text);
4.5
Ein ehat-Programm
141
input.add(button);
Contalner c = getContentPane();
e.add(top. BorderLayout.NORTH);
e.add(new JSerollPane(area). BorderLayout.CENTER);
e.add(input. BorderLayout.SOUTH);
// Verbindungsparameter werden bereitgestellt.
publie void init() {
if (frame ~~ null) (
host ~ getCodeBase().getHost();
port ~ Integer.parselnt(getParameter("port"));
else (
host ~ emdLineArgs[O];
port ~ Integer.parselnt(emdLineArgs[I]);
text.setEnabled(true);
text.requestFoeus();
button.setEnabled(true);
// Die gewünschte Benutzer-Aktion wird ausgeführt.
publie void aetionPerformed(AetionEvent e) (
Objeet obj ~ e.getSouree();
String emd ~ e.getAetionCommand();
try {
i f (obj ~~ button) {
i f (emd. equals ("Logi n"))
name ~ text.getText();
if (name.length() !~ 0) (
logi n();
}
else (
destroy() ;
i f (obj ~~ text) {
if (login) (
out.write(text.getText());
out. newL ine();
out. fl ush();
}
eateh (IOExeeption ex) {
area .append (ex. get M2ss age()
destroy() ;
+ "\ n") ;
4
142
Client/Server-Anwendungen mit TCP
finally (
text. setText("");
text.requestFocus();
// LOQln beim Server
private voi d login() throws IOException (
socket ~ new Socket(host. port);
in = new BufferedReader(new Input St reamReader (socket
.get lnputStream()));
out = new BufferedWriter(new OutputStreamWriter(socket
.getOutputStream()));
out.wrlte(name) ;
out.newLine() ;
out. fl ush() ;
login
=
true;
1abel .setText (name) ;
button. setText( "Logout" );
t ~ new Thread(this);
t.start() ;
// Logout beim Server
public void destroy()
if (login) {
try (
login ~ false;
1abel .setText(" ");
button. setText( "Logi n") ;
t ~ null;
if (socket !~ null)
socket.close();
if (in !~ null)
in.close();
i f (out !~ null)
out.close() ;
catch (IOException e)
public voi d run() {
try (
while (Thread.currentThread() ~~ t)
final String msg ~ in.readLine();
i f (msg ~~ null)
break;
4.6
Klassen über das Netz laden
143
doUpdat e(new Runnable ( ) {
public voi d run() {
eree.eppendtmsc + "vr"):
}
}) ;
}
catch O OExcepti on e ) {
pri vate voi d doupdetet gumeöle r ) {
try {
Eventüeue. i nvo Lel.ater(r) ;
catch (Exception e) {
'!lJ ehat -Client
Bild 4.6:
B
Chat-Client
Hligo
Hu g o
i ~t
dazug ~ k oMl1 ~n
Emi l
i~ t
d a zug ~k oMl1 ~n
Hu g o : Hall o ,
all~ ~
klar ?
Emi l : Natürlich'
I D a~
4.6
Pr ogr arm1
funktioni~r~
I[
l ogout
[
Klassen über das Netz laden
In Java müssen Klassen erst dann physisch vorhanden sein,
wenn sie verwendet werden sollen (dynamisches Laden von
Klassen). Ein so genannter Klassen/ader ist verantwortlich für das
Auffinden und Laden von Klassen.
Die Klasse jeva.: mg. Cl assLoader ist eine abstrakte Klasse. Sie etasstoader
enthält die Methode loadClass, die die Klasse mit dem angegebenen Namen lädt:
pub 1i c Cl ass<?> loadClass (Stri ng nare)
throws Cl assNotFound Excepti on
4
144
Client/Server-Anwendungen mit TCP
Ein eigener Klassenlader (als Subklasse von Cl assloader) kann z.
B. eine Klasse über das Netz laden.
Hierzu muss nur die ClassLoader-Methode
protected Cl ass<?> findClass(Strlng name)
throws ClassNotFoundExceptlon
in geeigneter Weise überschrieben werden .
Programm 4.4
Im Folgenden entwickeln wir einen Server (ClassServ er), der auf
Anfrage eine Klasse in seinem Serververzeichnis sucht und den
Inhalt als Byte-Array liefert.
Der Client (NetworkCl assloader) überschreibt als Subklasse von
ClassLoader die Methode fi ndC l ass. Dazu nutzt er die Cl assLoader-Methode defl neCl ass, um aus dem vom Server gelieferten
Byte-Array ein Cl ass-Objekt zu erzeugen,
protected flnal Cl ass <?> defineClass(
Str ing narre. byt e[] b , int off. int l en)
Achtung
Beim Einsatz eines Programms mit eigenem Klassenlader ist generell zu beachten,
Es wird versucht, weitere in der soeben geladenen Klasse referenzierte Klassen mit dem gleichen Klassenlader zu laden, mit
dem auch die erste Klasse geladen "WUrde.
Befinden sich das aufgerufene Programm (die Start-Klasse) und
die zu ladende Klasse im gleichen Klassenpfad. so wird diese
Klasse vom Standard-Klassenlader des Systems und nicht vom
eigenen Klassenlader geladen.
ClassServer
Der Server wird mit Portnummer und Suchpfad aufgerufen. Da
der Name der angeforderten Klasse auch den Paketnamen enthalten kann. werden Punkte durch Schrägstriche ersetzt und
somit die einzelnen Teile des Paketnamens auf Verzeichnisnamen abgebildet.
lmport
lmport
lmport
lmport
lmport
lmport
lmport
lmport
lmport
ja va
ja va
ja va
ja va
ja va
ja va
ja va
ja va
ja va
lO.Buffered Reader;
lO. Flle;
lo. Flle InputStream;
lo. IO Exceptlon;
lo. InputStreamReader;
lO.ObjectOutputStream;
net. InetAddress;
net.ServerSocket;
net.Socket;
4.6
Klassen über das Netz laden
145
publlC class ClassSer ver extends Thread
prlvate lnt port;
prlvate Strlng searchPath;
public ClassSer ver(int port. String searchPath) (
this.port ~ port;
thls.searchPath = searchPath;
public voi d startServer() {
try (
ServerSocket ser ver = new ServerSocket(port);
Inet Address addr ~ Inet Address .get Local Host ( ) ;
System.out.println("ClassServer auf"
+ addr. getHostNarre () + "/" + addr. getHostAddress ()
+ ".;" + port + " gestartet ... ");
whi 1e (true) (
Socket cllent = server accept();
new Cl assThread(client. searchPath).start() ;
}
catch (IOException e)
System.err.println (e);
pri vate class ClassThread extends Thread (
prlvate Socket ellent;
prl vate Strlng searchPath;
public ClassThread(Socket cli ent. String searchPath) (
thls.cllent = ellent;
thls.searchPath = searchPath;
public void run() (
String cli entAddr ~ client.get lnetAddress()
.get HostAddress ( ) ;
int clientPort ~ client.getPort();
System.out.println("Verbindung zu " + clientAddr
+ '";"
+ cl i ent Port + " aufgebaut");
try (
BufferedReader in ~ new BufferedReader(
new Input St reamReader (cl i ent .getl nput St ream( )) ) ;
ObjectOutputStream out ~ new ObjectOutputStream(client
.getOutputStream()) ;
out. fl ush();
String name
in.readLine();
4
146
Client/Server-Anwendungen mit TCP
name = narre. rep 1ace (' . " '/');
Strl ng cl assPath = searchPath + name + ". cl ass" ;
File file ~ new File(classPath);
int length ~ (int) file.length();
FilelnputStream fis ~ new FilelnputStream(file);
byte data[J ~ new byte[lengthJ;
l nt cnt
=
0;
while (cnt < length) (
int result ~ fis.read(data. cnt , length
if (result ~~-1)
break;
cnt += resu l t :
}
fis .close();
cnt);
if (data !~ null) (
out.writeObject(data);
out. fl ush () ;
in.close();
out.close() ;
System.out.println("\"" + classPath + "\" gesendet");
catch (IOException e) (
System.err.println(e);
finally {
try {
if (client !~ null)
cl i ent. cl ose();
catch (IOException e)
System.out.println("Verbindung zu " + clientAddr + ".;"
+ cl i entPort + " abgebaut");
public static void main(String[J args) {
if (args.length !~ 2) (
System.err
.prlntln("java ClassServer <port> <searchPath>");
System.exit(l);
int port ~ Integer.parselnt(args[OJ);
String searchPath ~ args[lJ;
searchPath
~
searchPath. rep 1ace(' \ \', '/');
4.6
Klassen über das Netz laden
147
i f (!searchPath.ends With ("j"))
searchPath += "I";
new ClassServer(port, searchPath).startServer();
lmport
lmport
lmport
lmport
import
import
java. io EOFException;
java. to . IOExcepti cn:
java. io ObjectlnputStream;
java. io. Pri nt WrHer;
java net. ConnectExcepti on;
java net.Socket;
NetworkClassLoader
public class NetworkClassLoader extends ClassLoader (
private String hast;
private int port;
public NetworkClassLoader(String host. int port) (
this.host ~ host;
this.port ~ port;
public Cl ass<?> findClass(String name) (
byte[] b ~ 10adClassData(name);
return defineClass(name. b. D. b.l engt h) ;
pri vate byte[] 10adClassData(String name) {
Socket socket ~ null;
try (
socket ~ new Socket(host. port);
ObjectlnputStream in ~ new ObjectlnputStream(socket
.get lnputStream());
PrintWriter out = new PrintWriter(socket
.getOutputStream(). true);
out.pr intln(name);
Object obj ~ in.readObject();
in.close() ;
out.close();
return (byte[]) obj;
catch (Connect Exception e) {
throw new RuntimeExcepti on("Verbi ndung zum Server "
"konnte ni cht hergeste11 t werden.");
catch (EOFException e) (
t hrow new RuntimeException(
"Kl asse wurde ni cht gefunden.");
+
4 Client/Server-Anwendungen mit TCP
148
catch (Exception e) (
throw new Runt l me Exception(e);
finally {
try {
if (socket !- null)
socket.close();
catch ( IOException e)
Statt
Das Programm Sta rt wird mit den folgenden Parametern aufgerufen:
•
Name bzw. IP-Adresse des entfernten Rechners
•
Portnummer des entfernten Rechners
•
Name der zu ladenden Klasse, die die ma in-Methode enthält
•
evtl, Aufrufparameter für die mai n-Methode
Das Reflection-APlwird genutzt, um die main-Mefhode der geladenen Klasse mit evtl. Parametern aufzurufen.
public class Start (
publ i c static void ma in(String [J args) {
if (args.l ength < 3) (
System.err.println(
"java Start <hast> <port> <className> [<paramI> ... ]");
System.exit(l);
String host - args[OJ ;
int port - Int eger .parsel nt (args [l J ) ;
String className - args[2J;
try (
NetworkClassLoader loader - new NetworkClassLoader(host.
port) ;
Cl ass<?> c = loader.loadClass(className);
// Bereitstellung der main-Methode
java.lang.reflect.Method m - c.getMethod("main".
new Class[J ( Str ing[J.class j);
Str ing[J params - new String[args.length
for (int i
params[i
=
3; i < args.l ength; i ++ )
3J - args[i J;
3J;
4.6
Klassen über das Netz laden
149
// Aufruf der main-Methode mit evtl. Parametern
m.invoke(null. new Objecte] ( params j);
catch (Runt imeExcept i on e) (
System.err.println (e.getMessage());
catch (Exception e) (
System.err.println(e);
Zur Demonstration des Verfahrens kann z. B. das Programm 4.3
(ChatCli ent) genutzt werden.
ClassServer
~~
ChatClient
~~
Bild 4.7'
Dynamisches Laden
über das Netz
I Start I
b yt e ] ]
I
I
Rechner A
Rechner B
10.108.105.96:40000
Das Laden unbekannter Klassen aus dem Netz stellt ein Sicherheitsrisiko dar. Eine Möglichkeit. sich zu schützen. besteht darin.
den Security Manager zu nutzen und nur so viele Zugriffsrechte
wie nötig für die Ausführung der Anwendung zuzulassen.
Die Zugriffsrechte. passend zum obigen Beispiel. werden in
einer Policy-Datei beschrieben:
grant {
permi ssi on ja va.1 ang.Runti mePermi ssi on "createCl assloader" ;
permission ja va.lang.RuntimePermission "exitVM";
permi ssi on ja va. awt .AWTPermi ssi on "shol'Mi ndol'Mi thoutWa rni ngBanner" ;
11 Cl assSer ver
permission ja va.net.SocketPermission "10.108.105.96: 40000" ,
"connect";
11 ChatServer
permission ja va.net.SocketPermission "10. 108.105.96:50000",
"connect";
{:
Aufruf des Start-Programms (Kommando in einer Zeile}
ja va -Djava.security.manage r -Djava.secu rity.policy= policy.txt
-cp bin Start 10 .108 .105 .96 40000 ChatCli ent 10.108.105.96 50000
policy.txt
4
150
4.7
Client/Server-Anwendungen mit TCP
Remote Procedure Call
In diesem Abschnitt entwickeln wir ein allgemeingültiges einfaches Verfahren zum Aufruf von Methoden, die auf einem entfernten Rechner implementiert sind (RPC = Remote Procedure
Call).
Programm 4.5
Einzige Voraussetzung ist: Die Parameterwerte einer entfernten
Methode müssen Objekte sein. Die Klassen aller Objekte (Rückgabewert und Parameterwerte) müssen das Interface ja va.l o.
Serl al i zabl e implementieren.
Der Client ruft die gewünschte Methode mit Hilfe von
Object call (String narre. Objecte] params)
der Klasse RPCCl i ent auf. narre ist der Methodenname, params
enthält die Parameterwerte.
Beispiel,
Lautet die Deklaration der entfernten Methode
public int getSumrre(lnteger x , Integer
s>.
so sieht der Codeabschnitt des Clients für den Aufruf beispielsweise so aus:
Objecte] params ~ (ID. 33);
i nt sumrre ~ ( Integer) rpcCl i ent ca11 ("getSumme". params);
RPCClient
Die Klasse RPCCl i ent nutzt die Methoden writeObject und readObject der Klassen ObjectOutputStream bzw. ObjecUnputStream.
um den Methodennamen, das Parameter-Array und den Rückgabewert über das Netz zu transferieren. Bei einem Rückgabewert
vom Typ Exceptlon wird eine Ausnahme dieses Typs ausgelöst.
l mport
l mport
l mport
l mport
java
java
java
java
i c. IOException;
i 0 ObjectInputStream;
iO.ObjectOutputStream;
net.Socket;
public class RPCClient
prlvate Strlng host;
prlvate lnt port;
public RPCClient(String host. int port) {
th is.host ~ host;
th is port ~ port;
4.7
Remote Procedure Call
pub1i c Object ca11 (Stri ng narre. Objecte] params)
throws Exception {
Socket socket ~ null;
try (
socket ~ new Socket(host. port);
ObjectOutputStream out ~ new ObjectOutputStream(socket
.getOutputStream()) ;
out writeObject(narre);
out writeObject(params);
out. flush();
ObjectlnputStream in ~ new ObjectlnputStream(socket
.getlnputStream());
Object ret ~ in.readObject();
in.close() ;
out.close();
lf (ret lnstanceof Exceptlon)
throw (Exception) ret;
return ret;
fina11y {
try {
i f (socket ! ~ null)
socket. cl ose () ;
catch (IOException e)
Die Klasse RPCServer nutzt das Reflection-Al'I. Sie lädt diejenige RPCSeroer
Klasse (hier als Service bezeichnet). die die Implementierung der
entfernten Methode enthält, und erzeugt eine Instanz dieser
Klasse. Dazu muss letztere Klasse den parameterlosen Konstruktor zur Verfügung stellen.
Anhand des Methodennamens und der Parameterwerte, die vom
Client als Objekte übermittelt wurden. wird mit Hilfe der ClassMethode getMethod die gewünschte Methode des Service als
Method-Objekt zur Verfügung gestellt.
Anschließend wird mit i nvoke die Service-Methode aufgerufen.
Wenn die Service-Methode eine Ausnahme auslöst, so löst
l nvoke die Ausnahme
java lang.reflect.InvocatlonTargetExceptlon
aus.
151
4
152
Client/Server-Anwendungen mit TCP
Die Invocatl onTargetExceptl on-Methode
Throwable getTargetException()
gibt die von der aufgerufenen Service-Methode ausgelöste Ausnahme ZUlÜCk.
Der RPCServer liefert Ausnahmen (Typ Exception), die beim Suchen bzw, bei der Ausführung der Service-Methode auftreten
können, als "normales" Ergebnisobjekt des Aufrufs zurück.
l mport
l mpor t
l mpor t
l mpor t
l mpor t
l mpor t
l mport
l mport
java
java
java
java
java
java
java
java
i c. IOException;
i 0 ObjectlnputStream;
io.ObjectOutputStream;
lang.reflect.InvocatlonTargetExceptlon;
lang.reflect.Method;
net.lnetAddress;
net.ServerSocket;
net. Socket;
public class RPCServer extends Thread (
prlvate int port;
private String service;
public RPCServer(int port. String service) (
this port ~ port;
this service = service;
public void startServer() throws Exception (
ServerSocket server = new ServerSocket(port);
InetAddress addr ~ InetAddress .getLocalHost();
System. out. printl n( "RPCServer auf" + addr. getHostName()
+ "/" + addr. getHostAddress() + ":" + port
+ " gestartet ... ");
System.out.println("Service: " + service);
Class<?> servlceClass = Class.forName(service);
Object serviceObject = servlceClass.newInstance();
whi le (true) (
Socket cllent = server.accept();
new RPCThread(client. serviceObject).start();
private class RPCThread extends Thread {
prlvate Socket cllent;
prlvate Object servlceObject;
4.7
Remote Procedure Call
public RPCThread(Socket client. Object serviceObject)
thls.cllent = client;
this.serviceObject = serviceObject;
public void run() {
try (
ObjectlnputStream in ~ new ObjectlnputStream(client
.getlnputStream());
String name ~ (String) in.readObject();
Object[] params ~ (Object[]) in.readObject();
Class<?>[] types ~ new Class[params.length] :
for (int i = 0; i < params.length; i++)
types[i] ~ params[i].getClass();
Object ret ~ null;
try (
Method m ~ serviceObject.getClass().getMethod(name.
types) ;
ret = m.invoke(serviceObject, params);
}
// Eine Ausnahme wird als Ergebnisobjekt
/ / zurückgel i efert
catch (InvocationTargetException e)
ret ~ e.getTargetException();
catch (Exception e) (
ret = e;
ObjectOutputStream out ~ new ObjectOutputStream(client
.getOutputStream()) ;
out.writeObject(ret);
out. fl ush();
in.close() ;
out.close();
catch (Exception e) (
System.err.println(e);
finally {
try {
if (cl ient !~ null)
cl ient.close();
catch (IOException e)
153
4
154
Client/Server-Anwendungen mit TCP
public static void main(String[] args) throws Exception {
if (args.length !- 2) (
System.err.prlntln("java RPCServer <port> <service>");
System.exit(l);
int port - Integer.parseInt(args[D]);
String service = args[l];
new RPCServer(port, servlce).startServer();
Test
Einige beispielhafte Service-Methoden sind in der Klasse DemoService implementiert. Test.Cl ient enthält den Aufruf dieser Methoden.
DemoService
import
import
import
import
import
java
java
java
java
java
iO.BufferedReader;
iO.FileReader;
io.IOException;
util.Date;
util .Vector;
pub 1i c cl ass I:€ITDSer vi ce (
public String getEcho(String text) (
return text;
public int getSumrre(lnteger x , Integer y) (
return x + y;
pub 1i c Date getOate ()
return new Date();
public void sendMessage(String msg)
System.out.println(msg);
public Vector<String> getMessages(String file)
throws IDException (
Vector<String> lines = new Vector<String>();
BufferedReader in - new BufferedReader(
new FileReader(file));
String line;
4.7
Remote Procedure Call
155
while ( (l i ne - in.read Line()) !- null) (
1i nes. add(l i ne);
)
in.close() ;
return 1i nes;
import java.utll.Date;
import java.utll.Vector;
TestClient
public class TestClient (
public static void main(String[J args) throws Exception (
String host - args[OJ;
int port - Int eger .parseI nt (args [I J ) ;
RPCClient rpcClien t - new RPCClien t(host. port);
Object[J params l - ( "Hallo" l:
String s - (St ri ng) rpcClient.call ("get Echo" . paramsl);
System. out. pr i nt 1n("getEcho; + s);
Object[J params2 - ( 10. 33 );
int summe - (Integer) rpcClient call("getSumrre". params2);
System. out. pr i nt 1n("getSumrre;
+ surrrne);
Object[J params3 - ();
Date date - (Date) rpcClient ca11 ("getOate". params3);
System. out. pr i nt 1n("getOate;
+ date);
Object[J params4 - ( "Dies ist ein Test." );
rpcClient.call ("sendl'essage". params4);
Object[J params5 - ( "rnsg. txt" );
@SuppressWarnings("unchecked")
Vector<String> v = (Vector<String»
rpcClient.call (
"geU1essages", params5);
System. out. pr i nt 1n("getHessages; ") ;
for (String msg v) (
System.out.println(msg);
Aufruf des Servers:
start ja va -cp bin RPCServer 50000 DemoSer vice
Aufruf des Client:
java -cp bin TestClient localhost 50000
4
156
4.8
Client/Server-Anwendungen mit TCP
Thread-Pooling
Die Fähigkeit des Servers, mehrere Clients quasi gleichzeitig zu
bedienen, wurde bisher so gelöst, dass für jede Anfrage eines
Clients ein neuer Thread erzeugt "WUrde. Im Vergleich zu Prozessen ist die Erzeugung eines Threads weniger aufwändig. Trotzdem sollte man hiermit sparsam umgehen; insbesondere dann,
wenn kurzzeitig sehr viele Threads mit kurzer Laufzeit benötigt
werden.
Tbread-Pool
Ab der Version Java SE 5 gibt es die Möglichkeit, einen ThreadPool einzusetzen. Dieser bietet die Möglichkeit, mehrere separate
Aufgaben vom selben Thread nacheinander ausführen zu lassen.
Nicht mehr benutzte Threads werden in den Pool zurückgelegt
und können wiederverwendet werden.
Wir benutzen hier eine spezielle Thread-Pool-Variante, einen so
genannten Cached Thread Pool, Ein solcher Pool wächst bzw,
schrumpft nach Bedarf Steht eine neue Aufgabe (hier eine
Client-Anfrage) zur Bearbeitung an und gibt es keinen "freien"
Thread im Pool, so wird ein neuer erzeugt, der die Ausführung
übernimmt Ist die Aufgabe ausgeführt, steht dieser Thread als
wieder "freier" Thread im Pool zur Verfügung. Wird er innerhalb
von 60 Sekunden nicht benötigt, so wird er terminiert und ist
dann nicht mehr verwendbar, Der Pool passt sich also dynamisch den momentanen Anforderungen an und ist optimal für
kleinere Aufgaben, die in hoher Zahl kutzfristig anstehen,
Für unsere Zwecke benutzen wir die Klasse ja va. uti l .
concurrent. Execut or s und das Interface java. uti l .concurrent.
Execut orServ l ce .
Die statische Execut ors-Metho de
statlc Execut or SerV l ce newCachedThreadPool( )
erzeugt einen Cached Thread Pool und liefert diesen als Objekt
vom Typ Execut orSerVlce zurück,
Das Interface Execut or Ser vl ce enthält u. a. die folgenden Methoden,
voi d execute(Runnable taskl
führt die r un-Methode von task in einem Thread des Pools aus.
voi d shutdown ()
bewirkt, dass vor dem Aufruf dieser Methode übergebene Aufgaben noch ausgeführt, neue aber nicht mehr akzeptiert werden;
alle vom Pool verwalteten Threads werden dann terminiert.
Damit ein Programm ordnungsgemäß beendet werden kann,
muss der Pool mit der Methode shutdo wn kontrolliert terminiert
werden.
4.8 Thread-Pooling
157
Programm 4.6 demonstriert den Einsatz eines Thread-Pools für Programm 4.6
einen Server.
lmport
lmport
l mport
lmport
lmport
lmport
lmport
java.lo BufferedReader;
java.lo.IOExceptlon;
j ava. jo . InputSt reamReader ;
java.lo.PrlntWrlter;
java net.ServerSocket;
java net.Socket;
java text.SimpleDateFormat;
import java.utll.Date;
import java.util.concurrent ExecutorService;
import java.util.concurrent Executors;
publie elass Server (
private int port;
private ExecutorService pool;
publie Server(int port)
this.port ~ port;
publie void startServer() {
try (
ServerSocket server = new ServerSocket(port);
System.out.println("Server gestartet ... ");
pool
=
Executors.newCachedThreadPool();
whi 1e (true) (
Socket client = server.accept();
pool .execute(new Handler(client));
}
eateh (IOExeeption e)
System.err.println(e);
pool.shutdown() ;
private elass Handler implements Runnable (
private Socket client;
publie Handler(Soeket elient)
this.client = client;
publie void run() {
try (
BufferedReader in ~ new BufferedReader(
new InputStreamReader(elient.getlnputStream()));
Server
4
158
Client/Server-Anwendungen mit TCP
PrintWriter out = new PrintWriter(client
.getOutputStream(). true);
String input ~ in.readLine();
if (input !~null) (
int delay ~ 5000 + (int) (Math.random()
long begin ~ 0;
long end ~ 0;
try (
begin ~ System.currentTimeMillis();
Thread.sleep(delay);
end ~ System.currentTimeMillis();
} catch ( InterruptedException e) (
)
Simpl eDateFormat f
~
*
5000);
new Simpl eOateFormat(
"Hii. nmss") ;
out println("Auftrag " + input + ". Beginn:
+ f.forma t(new Oate(begin)) + ". Ende ;
+ f.forma t(new Oate(end)));
in.close();
out.close() ;
catch ( IOException e)
System.err.println(e);
finally (
try (
if (client !~ null)
cl i ent. cl ose();
catch (IOException e)
public static voi d main(String[] args)
int port ~ Int eger .parsel nt (args [ O] ) ;
new Server(port).startServer();
4.9
Ein Framework für TCP-Server
Die Beispiele der vorhergehenden Abschnitte zeigen, dass die
Implementierungen der TCP-Server im Großen und Ganzen fast
immer dem gleichen Muster folgen. Es liegt also nahe, die immer
wiederkehrenden Codeteile zu standardisieren und als Framework für eigene Server-Implementierungen anzubieten.
4.9
Ein Framework für TCP-Server
159
Das Framework besteht aus den beiden Klassen TCPServer und Programm 4.7
AbstractHandler. die zum Paket tcpfrarrework gehören.
Der Konstruktor von TCPServer erwartet eine Portnummer und
die Klasse des Handlers, der die eigentliche Kommunikation mit
dem Client durchführt. Hier wird das Class-Objekt des Handlers
angegeben. Der Handler ist eine Subklasse von AbstractHandler.
Innerhalb des Konstruktors werden ein ServerSocket-Objekt und
ein Thread-Pool (siehe Kapitel 4.8) erzeugt.
TCPServer ist von Thread abgeleitet. Innerhalb der run-Methode
wird in einer Schleife die Methode accept aufgerufen. Mit dem
zurückgegebenen Socket-Objekt wird die handle-Methode aufgerufen. Diese erzeugt eine neue Instanz des Handlers und ruft für
diese Instanz die Methode handle mit den Referenzen für das
Socket-Objekt und den Thread-Pool auf.
Die Methode stopServer schließt das ServerSocket-Objekt und
terminiert den Thread-Pool.
package tcpframework;
lmport
lmport
lmport
lmport
l mport
l mport
java.lo.IOExceptlon;
java net.ServerSocket;
java net.Socket;
java net.SocketExceptlon;
j ava. ut II .concurrent. ExecutorSerVl ce;
j ava. ut II .concurrent. Executors ;
public class TCPServer extends Thread
prlvate Cl ass<?> handlerClass;
prlvate ServerSocket serverSocket;
prlvate ExecutorSerVlce pool;
public TCPServer(int port. Class<?> handlerClass)
throws IOException (
this.handlerClass ~ handlerClass;
serverSocket = new ServerSocket(port);
pool ~ Executors.newCachedThreadPool ();
public void run() {
try {
whi 1e (true) (
// Belm Aufruf von stopServer() wlrd elne
// SocketException ausgelöst
Socket cllentSocket = serverSocket accept();
handle(clientSocket);
}
catch (SocketException e) {
TCPServer
4
160
Client/Server-Anwendungen mit TCP
catch (Exception e) (
System.err.println(e);
public voi d stopServer()
try (
serverSocket.close();
} catch ( IOException e)
}
poo 1.shutdown () ;
pri vate void handle(Socket cli entSocket) throws Exception (
AbstractHandler handler ~ (AbstractHandler) handlerClass
. newInstance();
handl er. handl e(c 1i entSocket. pool);
Ein konkreter Handler ist von AbstractHandler abgeleitet und implementiert die run-Methode. Diese wird durch die handleMethode in einem Thread des Pools ausgeführt. Zudem wird das
Socket-Objekt mit Hilfe der Methode getCl i entSocket zur Verfügung gestellt.
AbstractHandler
package tcpframework;
lmport java net.Socke t;
lmport java utll .concurrent ExecutorSerVlce;
public abstract class AbstractHandler implements Runnable (
prlvate Socket cllentSocket;
public Socket getClientSocket()
return cllentSocket;
public void handle(Socket cl i entSocket.
ExecutorService pool) (
this.clientSocket ~ cl i entSocket;
pool.execute(this);
public abstract void run();
4.9
Ein Framework für TCP-Server
161
Das Framework wird nun in einem Beispiel verwendet. Hierbei Anwendung des
handelt es sich um den Echo-Server aus Kapitel 4.3.
Frameworks
lmport java.lo. IOExceptlon;
EchoServer
lmport tcpframework .TCPServer;
public class EchoSer ver (
public static void main(String[] args) throws IOExcept i on (
int port ~ Int eger .parsel nt( args [O] ) ;
TCPServer ser ver
=
new TCPServer (port , EchoH andl er class);
server .start();
System.out.println("Server gestartet ... ");
// blockiert. bis RE TURN eingegeben wurde
System. in. read () ;
server.stopServer();
System.out.print l n("Server wird gestoppt ... ");
Der Server wird nach Betätigung der Return-Taste gestoppt;
allerdings erst dann, wenn alle momentan aktiven ClientBearbeitungen beendet sind.
lmport
lmport
l mport
lmport
lmport
java.lo BufferedReader;
java.lo. IOExceptlon;
j ava. jo . InputSt reamReader ;
java.lo. PrlntWrlter;
java net.Sock et;
EchoHandler
import tcpframework.Abstract Handl er;
public class EchoHandl er extends AbstractHandler
public void run() (
Socket clientSocket ~ getCli entSocket();
String clientAddr ~ clientSocket.get lnetAddress ()
.get HostAddress();
int clientPort ~ clientSocket.getPort();
System.out.print ln("Verb indung zu " + clientAddr +
'";"
+ c II entPort + " aufgebaut");
try (
BufferedReader in ~ new BufferedReader(new
Input St reamReader (cl ie nt Socket .getlnput St ream( ) ) ) ;
4
162
PrlntWrlter out
=
Client/Server-Anwendungen mit TCP
new PrlntWrlter(cllentSocket
.getOutputStream(). true);
out prlntln("Server ist berelt ... ");
String input;
while ((input ~ in.readLine())
out. pri nt 1n(i nput);
!~
null) {
in.close();
out.close() ;
catch ( IOException e)
System.err.println(e);
finally {
try {
if (cl ient Socket !~ null)
clientSocket.clos e();
catch ( IO Exception e ) (
System.out.println("Verbindung zu " + clientAddr + ".;"
+ cl i entPort + " abgebaut");
Um mit dem Client zu kommunizieren, wird in der run-Methode
das Socket-Objekt über die Methode getel i entSocket erfragt,
Dann erfolgt das Empfangen und Senden der Daten.
4.10
1.
Aufgaben
Entwickeln Sie einen TCP-SelVer, der als Reaktion auf die
Verbindungsaufnahme durch den Client an diesen einen in
einer Datei gespeicherten Text sendet und dann von sich
aus die Verbindung beendet.
Testen Sie den Server mit Hilfe von Telnet:
telnet localhost 50000
2.
Entwickeln Sie einen TCP-SelVer, der die aktuelle Systemzeit
des Servers als Zeichenkette liefert. Programmieren Sie auch
einen passenden Client hierzu.
3.
Entwickeln Sie einen Echo-Server, der die parallele Bedienung mehrerer Clients ermöglicht, aber die Anzahl der
4.10 Aufga ben
163
gle ich zeitig ab..rlve n Threacls auf eine vorgegebene Zahl b esch rä nkt.
4.
Entwicke ln Sie für de n File-Se rver aus Kap itel 4.4 eine n
Client mit g rafische r Oberfläche.
'" """""
,021_................
__ __ ~
10 3_1UJpl)1.....
;f U _~
.
1llS_1UJpl)1&d
06)1.).1.....
;1l 1_~
~
~'"
Bild 4.8 :
Rk-Cli e71l
~
)
1-11 - 1
I
5.
I
Aus eine r Büch er-Dare nba nk so Uen zu e ine r vo rgegebe nen
Buch nu mme r Anga be n zu m Buch (Auto r und Ti tel) über
SQL abge fragt we rden. Nutzen Sie das RPC-Verfuh ren a us
Kapitel 4.7 und entwickeln Sie hierzu eine n Service mit der
Methode
publi c Buch getBuch(1nteger 'i d) thrtws Except t on
und eine n Client , de r diese entfernte Methode a ufruft .
Die Klasse Buch soll die Angabe n zum Buch als Attribute mit
den entsp reche nden set- und get-Merhoden enthalten .
6.
Angelehnt a n das Beispiel des Kapitel s 2.8 soll ein Server
auf Anfrage (Lade n) eine CachedROtJSet -Insta nz an den Client
üb ertragen. Diese r soll die Daten in Form einer Tabelle an zeigen (Bild 4.9). Der Besta nd zu einem Buchtitel kann ge än de rt we rde n. Mit "Speichern" soll die Cach edRCl.\ISe t -Instanz
zum Server ge schi ckt und die Änderungen in der Datenbank e ingespielt we rden.
164
Md 4.9:
Client
4 Client/Server-Anwendungen mit TCP
Clie nt
___I S.~ tJ
3-15-001308-9
3-15-001562-6
3-15-010606-0
3-257 -20998-3
3-257-21034-5
3-257-21166-X
3-257-21405- 7
3-257-21406-5
3-351 -030 44-(
H58-32655-3
H58-32733-9
H58-32810-6
H58-33004-6
H9 1-96007-X
3-538 -05349-9
3-538 -06656-6
3-538 -06657-(
3-538 -06658-2
:lo.5..3.&OJi9..820
St. nd
23 200 9-07-1 4 18:03:0
102009-07-14 17:50 :3
122009-07-101 UH
132009-07-08 17:48:3
552009-07-0800:00 :0
82009-07-0800:00 :0
332009-07-0800:00 :0
82009-07-0800:00 :0
32 2009 -07-08 1 7 : ~8: 3
302009-07-0800:00 :0
152009-07-0800:00 :0
162009-07-0800:00 :0
202009-07-0800:00 :0
102009-07-0800:00 :0
122009-07-0800:00 :0
22 2009 -07-08 00:00 :0
102009-07-0800:00 :0
152009-07-0800:00 :0
Titel
Schwe re lerten
Groß e Erw. rtunQen
Der W eihnachts abe
Niko las Nick leb y
David Copp erfield
Ble. kh.u s
Die Pickw ick ier
M. rtin Chuzzlew it
W eihnachten m it Di
Harte Ze iten
Gesc hichte aus LW
Ble. k House
Nikol. us Nickleby
W eihnachts erzählu
David Copp erfield
Die Pickw ick ier
Martin Chuzzlew it
Nicho la s Nick leby
ctlQJatlIkkla
I
Laden
o..9o.OLQ.8.o.Q:O
11
Speichern
Herunterladen