Server - oth-regensburg.de

Werbung
VS: Verteilte Systeme
Kap. 02 Kommunikation
Prinzipien und Realisierung mit Sockets
Wolfgang Wiedermann
FH Regensburg FH
Teil 1
1
Inhalt: Kommunikation
„
„
Übersicht: Schichten
Sockets
„
„
„
„
„
„
„
„
Verbindungsfreie Sockets: UDP-Sockets
Verbindungsorientierte Sockets: TCP-Sockets
Marshalling, Unmarshalling
Request/Reply-Protokolle
RPC Remote Procedure Call
Nachrichten-Orientierte Kommunikation MOM
Stream-Orientierte Kommunikation
Literatur
„
„
Couloris/Dollimore/Kindberg (cdk) : VS
Tanenbaum (tb): VS
2
Schichten der Middleware
Anwendungen, Dienste
RMI, RPC, CORBA, … Web-Services
Request-Reply … Protokolle
UDP und TCP
Middleware
3
Sockets: Allgemein
„
„
„
„
„
Das Socket-Interface wurde zur Programmierung verteilter
Anwendungen auf TCP/IP-Netzwerken für UNIX-Rechner
entwickelt.
Die Socket-Abstraktion setzt zwei Prozesse voraus, die auch
auf demselben Rechner laufen können.
Ein Socket ist dabei als ein Endpunkt einer Kommunikation im
Netzwerk zu verstehen.
Um eine Verbindung zu ermöglichen, braucht man zwei
solcher Enden (Æ port)
Dabei gibt es zwei Typen von Verbindungen
„
verbindungsfrei:
„
„
Datagramme, einzelne Pakete von Nachrichten (UDP)
verbindungsorientiert:
„
sequenzielle Bearbeitung in Streams möglich (TCP)
4
Sockets und Ports
socket
any port
agreed port
socket
message
client
server
other ports
Internet address = 138.37.94.248
Internet address = 138.37.88.249
Grafik: cdk
5
Kommunikation mit verbindungsfreien Sockets
„
„
Mit verbindungsfreien Sockets können einzelne Datenpakete
versandt werden.
Transfer
„
„
„
„
Der empfangende Prozess muss ein Byte-Array zur Verfügung stellen,
in das die Nachricht passt. Ist die Nachricht zu lang, so wird sie
gekürzt.
Senden (send) blockiert nicht, empfangen (receive) blockiert, außer
wenn man einen Time-Out angibt.
Die Empfangsmethode receive nimmt jede Nachricht an, die an den
Socket adressiert ist. Der Absender kann aus der Nachricht ausgelesen
werden.
Fehlermöglichkeiten
„
„
„
Jedes Paket ist unabhängig von den anderen.
Pakete können verloren gehen.
Einzelne Pakete können andere Pakete überholen.
6
Trivialansatz Remote-Kommunikation
Client
Server
(Daten in String schreiben)
On Interrupt: String parsen
Request
message
Daten verarbeiten
(warten)
(Reply Message auswerten)
Reply
message
Daten in String schr.
Request
Parameter1
Parameter2
Parameter3
…
7
Trivialansatz Remote-Kommunikation
Request
Parameter1
Parameter2
Parameter3
…
Problem:
So ist nur eine Funktion pro Port möglich!
8
Trivialansatz Remote-Kommunikation
Request
Parameter1
Parameter2
Parameter3
…
Problem:
So ist nur eine Funktion pro Port möglich!
Lösung:
OperationsID zum Request hinzufügen
9
Trivialansatz – leicht verbessert
Client
Server
(Operation
+Daten in String schreiben)
On Interrupt: String parsen
Request
message
Operation ausw.
(warten)
(Reply Message auswerten)
Daten verarbeiten
Reply
message
Daten in String schr.
Request
OperationID
Parameter1
Parameter2
…
RetParam2
RetParam3
…
Reply
RetParam1
10
Trivialansatz – leicht verbessert
Request
OperationID
Parameter1
Parameter2
…
RetParam2
RetParam3
…
Reply
RetParam1
Problem:
Keine Zuordnung zwischen Request und Reply!
(Ist nur bei nicht verbindungsorientierten, also UDP-basierten Protokollen relevant)
11
Trivialansatz – leicht verbessert – Version 2
Client
Server
(Operation
+Daten in String schreiben)
On Interrupt: String parsen
Request
message
Operation ausw.
(warten)
(Reply Message auswerten)
Daten verarbeiten
Reply
message
Daten in String schr.
Request
OperationID
RequestID
Parameter1
…
RetParam1
RetParam2
…
Reply
RequestID
12
Objektorientierung vs. Remote Function Call
Prozedurales Remoting
• Aufruf von entfernten Funktionen oder Prozeduren
• Alle relevanten Daten werden als Parameter übergeben
• per Definition Stateless
Objektorientiertes Remoting
• Aufruf von entfernten Methoden
• Diese gehören per Definition zu Objekten
• deshalb Stateful
• Problem: Identifikation von entfernten Objekten
13
Request-Reply-Protokolle und Objektreferenzen
Client
Server
Request
doOperation
getRequest
message
Auswahl des Objekts
(warten)
Reply
message
(continuation)
Ausführung der Methode
sendReply
Objektreferenz
32 bits
32 bits
Internet Adresse Port
32 bits
Zeit
32 bits
Objektkennung Schnittstelle
Grafik: cdk
14
Operationen in einem Request-Reply-Protokoll
„
Senden einer Anforderung an ein entferntes Objekt.
Parameter: Objektreferenz, Methodenreferenz, Argumente
public byte[] doOperation (
RemoteObjectRef o, int methodId,
byte[] arguments)
„
Auslesen einer Anforderung vom Server-Port
public byte[] getRequest ();
„
Senden einer Antwort an den Client.
public void sendReply (
byte[] reply, InetAddress clientHost,
int clientPort);
15
Aufbau einer Request-Reply-Nachricht
messageType
int (0=Request,1= Reply)
requestId
int
objectReference
RemoteObjectRef
methodId
int or Method
arguments
array of bytes
Grafik: cdk
16
Marshalling, Unmarshalling
„
„
„
Bei verteilten Systemen müssen Daten von einem Rechner zu
anderen Rechner übertragen werden.
Dazu müssen sie in eine für die Übertragung geeignete Form
umgewandelt bzw. aus dieser Form wieder in eine für den
Zielrechner brauchbare Form zurückgewandelt werden.
Marshalling:
„
„
Wandelt eine Kollektion von Datenelementen in eine für die
Übertragung geeignete Form um (serialisieren).
Unmarshalling:
„
Zerlegt empfangene Daten und erzeugt daraus beim Empfänger eine
identische Kollektion von Datenelementen (deserialisieren).
17
Marshalling
„
Private Datenformate
„
„
„
„
Für kleine Anwendungen kann man private Datenformate für die
Abspeicherung erfinden.
Z.B. kann man alle Daten als Text abspeichern, wobei die Felder durch
Sonderzeichen getrennt werden.
Dieses Verfahren stößt schnell an seine Grenzen.
Serialisierung
„
„
„
In diversen Frameworks gibt es für manche Klassen die Möglichkeit,
Objekte zu serialisieren.
Die Objekte werden auf Streams geschrieben.
Diese Streams passen nahtlos zur verbindungsorientierten
Kommunikation mit Sockets und über ByteStreams auch zu
Datagrammen.
18
Marshalling : Datenformate
„
„
„
„
„
XDR (Internet RPC External Data Representation)
NDR (Network Data Representation vom MS -RPC)
CDR (Common Data Representation CORBA)
Java object serialization
ASN.1 (ISO OSI) :
Grafik: cdk
19
Marshalling über XML
„
„
Manche Frameworks bieten die Möglichkeit, die Daten in
jeweils eigenen XML-Formaten zu serialisieren.
Java und .NET bieten hier eingebaute Möglichkeiten
„
Objekt Æ XML
„
„
„
XSD Æ Klasse, Objekte dieser Klasse dann in XML serialisieren.
„
„
„
Bleibt man in einem Framework, gibt es kein Problem beim
Marshalling/Unmarshalling
Der Austausch mit anderen Systemen ist häufig zum Scheitern verurteilt.
XML-Format via XSD normiert, d.h. gute Aussichten, Daten über
Framework-Grenzen hinweg auszutauschen.
ABER: Die Klassen in Java/C# werden generiert und dürfen danach nicht
mehr verändert werden.
Siehe: vs-xml-persistenz.pdf
20
Probleme mit der Kommunikation über Sockets
„
„
„
„
„
Marshalling/Unmarshalling aufwendig
Asynchrone Kommunikation fehleranfällig
Die asynchrone Kommunikation passt nicht in das „normale
Programmiererleben“
Abhilfe: RPC Remote Procedure Call
Die Middleware übernimmt
„
„
„
„
„
„
Marshalling/Unmarshalling,
Transport,
Zustellung,
Aktivierung des Servers und
Antworten.
Beispiel: ein Aufruf k = add (i, j) als RPC
21
Client
Aufruf:
k = add (i, j);
#add
Server
2
1
Kennzeichen für Methode,
Werte der Parameter (27 bzw. 2)
auf Verbindung schreiben
00.00.00.1B
Server horcht an Socket
3
00.00.00.02
4
Auspacken der Parameter
Ergebnis als Rückgabewert setzen
9
00.00.00.1D
X0 im RAM
X1 im RAM
X2 im RAM
??.??.??.??
00.00.00.1B
00.00.00.02
X0 = add (X1, X2)
8
5
00.00.00.1D
6
7
Programmcode der
add (int, int)Methode
RPC: Schritt 1 und 2
„
Zu 1:
„
„
„
Der Client kann die Methode add nicht direkt aufrufen, da sie in einem
anderen Prozess implementiert ist.
Stattdessen wird eine add-Methode eines Stellvertreters aufgerufen.
Zu 2:
„
„
„
„
Diese add-Methode öffnet einen Kommunikationskanal zu einem Server
(z.B. über einen Socket, schreibt einen Auftragscode für das
gewünschte Objekt, die Verarbeitung sowie die Parameter auf den
Socket.
Die Darstellung der Daten muss unabhängig von der Plattform sein.
Z.B. kann der Empfänger ein Big-Endian-Computer (Z.B. Sparc:
höchstwertiges Byte zuerst) sein, der Sender ein Little-EndianComputer (Z.B. Intel, niederwertiges Byte zuerst).
Danach blockiert diese Methode im Stellvertreter.
23
RPC: Schritt 3 und 4
„
Zu 3
„
„
„
„
„
Der Kommunikationskanal übernimmt den Transport zum Server.
Der Server muss zu diesem Zeitpunkt gestartet sein und z. B. an einem
Socket „horchen: listen-Operation. Eine eingehende Verbindung
wird akzeptiert.
Der Server kann die Aufträge sequentiell bearbeiten oder auch parallel.
In letzterem Fall bietet sich die Zuordnung der Arbeiten zu einzelnen
Threads an, die je nach Bedarf vorher gestartet wurden (sog. ThreadPool), bzw. in Abhängigkeit von einem Auftrag gestartet werden.
Zu 4:
„
„
„
In jedem Fall müssen die Parameter ausgepackt werden.
Sie werden aus der netzwerkunabhängigen Darstellung in die binäre
Darstellung der Server-Plattform konvertiert und dort im RAM abgelegt.
Auch der Platz für ein eventuelles Ergebnis wird bereitgestellt.
24
RPC: Schritte 5 … 9
„
Zu 5
„
„
„
Zu 6:
„
„
Das Ergebnis wird an den Auftraggeber zurückgeliefert.
Zu 8:
„
„
Das Ergebnis wird in den Kommunikationskanal geschrieben.
Zu 7:
„
„
Die gewünschte Routine wird zum Ablauf gebracht.
Das Ergebnis liegt nun im RAM (Server) vor.
Die Daten werden aus dem Socket ausgelesen und im RAM-Bereich des
lokalen Stellvertreters abgelegt.
Zu 9:
„
Der Wert wird als Ergebnis des Aufrufs zurückgeliefert.
25
Implementierung von RPC-Servern
„
Sequentieller Server
„
„
Problem
„
„
Der Server ist in der Lage, mehrere Aufträge parallel auszuführen, z.B.
über Threads.
Problem
„
„
Lange Wartezeiten für die Auftraggeber, auch dann, wenn der Server
nicht ausgelastet ist.
Paralleler Server
„
„
Wenn ein Server bei der Ausführung eines Auftrags in einen
Wartezustand gerät, kann er nicht auf andere Aufträge reagieren.
Wer übernimmt die Synchronisierung auf der Server-Seite?
Microsoft-Bezeichnungen
„
Single-Threaded-Apartment STA und Multi-Threaded-Apartment MTA
26
Problem beim RPC
„
„
„
Die Schnittstelle zwischen Client und Server ist so komplex, dass sie
maschinell unterstützt werden muss.
So muss der Code für das Ver- und Entpacken der Parameter generiert
werden. Im Grunde muss ein Vertrag zwischen Client und Server
aufgesetzt werden.
Aus dem Vertrag heraus müssen die entsprechenden Teile für Client- und
Server generiert werden:
[
uuid(7a98c250-6808-11cf-b73b-00aa00b677a7),
version(1.0)
]
interface hello{
void HelloProc([in, string] unsigned char * pszString);
void Shutdown(void);
}
27
Entwicklung für RPC
„
„
„
„
„
Der Vertrag wird in IDL
aufgesetzt.
Client- und Server-Anteile werden
generiert.
Die Anwender können ihre
Software entwickeln.
Die Software läuft dann mit dem
Laufzeitsystem der Middleware.
Also: Die Middleware greift auch
in den Entwicklungsprozess für
die Software ein.
Clientseite
Serverseite
Entwickler:IDL
Schnittstelle
Client:
Stellvertreter,
Stub
Client:
Anwendung
Server:
Skeleton
Server:
Funktionalität
28
Entfernter Objektaufruf
Grafik: ts
29
Sockets und RPC: Zusammenfassung
„
„
Client-/Server-Modelle
Der Server muss laufen, wenn der Client eine Anfrage absetzt.
„
„
„
„
Æ Was ist mit Geräten, die nicht permanent eingeschaltet oder
verbunden sind?
Dies gilt für Clients (Auftrag absetzen und beenden) ebenso wie für
Server (läuft nicht ständig)
Abhilfe: eine Message-Oriented-Middleware MOM mit der Fähigkeit zur
Pufferung der Aufträge, sei es auf der Seite des Clients oder der des
Servers.
Sockets können auch für eine asynchrone Kommunikation
benutzt werden, der klassische RPC nicht.
„
CORBA bietet für Objekte eine asynchrone Kommunikation mit seinen
oneway-Methoden an (fire and forget).
30
Nachrichtenorientierte Kommunikation
Grafik: ts
31
Nachrichtenorientierte Kommunikation
„
Beispiele
„
„
„
„
Email
SMS
JMS Java Messaging Service in JEE
Prinzip
„
„
Die Middleware nimmt die Aufträge entgegen und sorgt für die
Zustellung.
Wenn sich ein Client beenden will:
„
„
Wenn ein Server nicht läuft:
„
„
Die Aufträge liegen auf einem Kommunikationsserver und können
zugestellt werden. Hier ist nur eine asynchrone Arbeitsweise sinnvoll.
Auch hier liegen die Aufträge auf einem Kommunikationsserver und können
zugestellt werden, sobald sich der Server als aktiv meldet.
Die Steuerung kann die Kontrolle über die Server übernehmen
und z.B. Transaktions-Dienste implementieren.
32
Streamorientierte Kommunikation: Übersicht
Grafik: ts
33
Streamorientierte Kommunikation
„
Problem:
„
„
Wenig Steuer-Fluss, dafür ein hohes Datenaufkommen, meist in eine
Richtung.
Bei den Daten werden QoS (Quality of Service)-Anforderungen gestellt.
„
„
„
Filme dürfen nicht ruckeln, 30 Frames/sec.
Audio muss kontinuierlich übertragen werden: PCM 1/44100 sec. Intervalle
bei Playback.
Die streamorientierte Kommunikation lässt sich nicht mit den
bisher besprochenen Middleware-Techniken implementieren,
bei denen Zeit keine Rolle spielt.
34
Streamorientierte Übertragungsarten
„
Asynchrone Übertragung
„
„
„
Synchrone Übertragung
„
„
„
„
Eine maximale End-To-End-Verzögerung ist definiert, die Daten dürfen
aber auch schneller übertragen werden.
Isochrone Übertragung
„
„
Beispiel: File-Transfer
Die Daten werden ohne Zeitbedingungen sequentiell übertragen.
Es gibt eine minimale und maximale End-to-End-Verzögerung, sog.
Beschränkter Jitter.
Dies ist eine wichtige Übertragungsart für Mulitmedia.
Einfache Streams: nur eine Quelle für Daten, z.B. Sprache.
Zusammengesetzte Streams: mehrere Quelle für Daten, z.B.
lippensynchrone Übertragung von Ton und Bild.
35
VS:
Verteilte Systeme
Prof. Dr. Fritz Jobst
FH Regensburg
FB Informatik/Mathematik
Sockets in Java
Architektur von Socket-Lösungen
36
Inhalt
„
Sockets und Ports
„
„
„
System-unabhängige Datendarstellung in Java
„
„
„
Die Klassen DataInputStream und DataOutputStream
Abbildung auf Streams (Æ TCP)
Beispiel: Additions-Server für TCP
„
„
Verbindungsorientierte Sockets
Verbindungsfreie Sockets
Architektur
Kurzbeschreibungen für die Klassen
„
„
Socket, ServerSocket
InetSocketAddress, URL
37
Sockets: Allgemein
„
„
„
„
„
Das Socket-Interface wurde zur Programmierung verteilter
Anwendungen auf TCP/IP-Netzwerken für UNIX-Rechner
entwickelt.
Die Socket-Abstraktion setzt zwei Prozesse voraus, die auch
auf demselben Rechner laufen können.
Ein Socket ist dabei als ein Endpunkt einer Kommunikation im
Netzwerk zu verstehen.
Um eine Verbindung zu ermöglichen, braucht man zwei
solcher Enden (Æ port)
Dabei gibt es zwei Typen von Verbindungen
„
verbindungsfrei:
„
„
Datagramme, einzelne Pakete von Nachrichten (UDP)
verbindungsorientiert:
„
sequenzielle Bearbeitung in Streams möglich (TCP)
38
Sockets und Ports
socket
any port
agreed port
socket
message
client
server
other ports
Internet address = 138.37.94.248
Internet address = 138.37.88.249
Grafik: cdk
39
Kommunikation mit verbindungsfreien Sockets
„
„
Mit verbindungsfreien Sockets können einzelne Datenpakete
versandt werden.
Transfer
„
„
„
„
Der empfangende Prozess muss ein Byte-Array zur Verfügung stellen,
in das die Nachricht passt. Ist die Nachricht zu lang, so wird sie
gekürzt.
Senden (send) blockiert nicht, empfangen (receive) blockiert, außer
wenn man einen Time-Out angibt.
Die Empfangsmethode receive nimmt jede Nachricht an, die an den
Socket adressiert ist. Der Absender kann aus der Nachricht ausgelesen
werden.
Fehlermöglichkeiten
„
„
„
Jedes Paket ist unabhängig von den anderen.
Pakete können verloren gehen.
Einzelne Pakete können andere Pakete überholen.
40
Verbindungsfreie Sockets in Java: Übersicht
Client2
Server1
DatagramSocket
DatagramPacket
Client1
DatagramSocket
DatagramSocket
DatagramPacket
DatagramPacket
Server2
DatagramSocket
Jedes DatagramPacket kann
an einen anderen Server
geschickt werden!
DatagramPacket
41
Aufbau eines Clients (UDP) nach cdk
import java.io.*;
import java.net.*;
public class UDPClient {
public static void main(String args[]) {
// args give message contents and server hostname
DatagramSocket aSocket = null;
try {
aSocket = new DatagramSocket();
byte[] m = args[0].getBytes();
InetAddress aHost = InetAddress.getByName(args[1]);
int port = 6789;
DatagramPacket request=new DatagramPacket(m, m.length, aHost, port);
aSocket.send(request);
byte[] buffer = new byte[1000];
DatagramPacket reply = new DatagramPacket(buffer, buffer.length);
aSocket.receive(reply);
System.out.println("Reply: " + new String(reply.getData()));
} catch (SocketException e) {
System.out.println("Socket: " + e.getMessage());
} catch (IOException e) {
System.out.println("IO: " + e.getMessage());
} finally {
if (aSocket != null)
aSocket.close();
}
}
}
42
Aufbau eines Servers (UDP) nach cdk
import java.io.IOException;
import java.net.*;
public class UDPServer {
public static void main(String args[]) {
DatagramSocket aSocket = null;
try {
aSocket = new DatagramSocket(6789);
byte[] buffer = new byte[1000];
while (true) {
DatagramPacket request = new DatagramPacket(buffer, buffer.length);
aSocket.receive(request);
DatagramPacket reply = new DatagramPacket(request.getData(), request
.getLength(), request.getAddress(), request.getPort());
aSocket.send(reply);
}
} catch (SocketException e) {
System.out.println("Socket: " + e.getMessage());
} catch (IOException e) {
System.out.println("IO: " + e.getMessage());
} finally {
if (aSocket != null)
aSocket.close();
}
}
}
43
Verbindungsfreie Sockets und Daten in Java
„
„
„
„
„
„
„
Problem: Datagramme senden bzw. empfangen nur BytePuffer: byte[].
Die Daten sollten mit den Klassen DataInputStream bzw.
DataOutputStream gelesen bzw. geschrieben werden.
Zum Senden können die Daten in einen DataOutputStream für
einen ByteArrayOutputStream geschrieben werden.
Dieser Stream wird dann als Byte-Array ausgelesen und
geschrieben.
Analog: Man empfängt einen Byte-Array.
Dieser wird als ByteArrayInputStream an einen
DataInputStream übergeben und von da mit DataInputStream
ausgelesen.
Strings: readUTF bzw. writeUTF benutzen.
44
UNIX-BSD-Sockets: Abläufe
Server
socket()
bind()
Client
listen()
socket()
accept()
connect()
recv()
send()
send()
recv()
45
Verbindungsorientierte Sockets 1
„
„
„
„
„
Bei der Verbindung zweier Prozesse sind die Rollen von Client
und Server festgelegt.
Jeder Socket hat einen Ausgabe- und einen Eingabestream.
Senden erfolgt durch Schreiben in den Stream, Empfangen
durch Lesen aus dem Stream.
Der Schreiber kann blockiert werden, wenn er für den Leser
zu schnell ist.
Schließen eines Sockets:
„
„
„
Daten werden an den Empfänger gesendet.
Kennzeichen über das Schließen der Verbindung wird gesendet.
Beim Ende eines Prozesses werden alle seine Sockets geschlossen.
46
Verbindungsorientierte Sockets 2
„
Fehlerbehandlung
„
„
„
Prozesse können nicht zwischen Netzwerkfehlern und Fehlern in den
Partner-Prozessen unterscheiden.
Die kommunizierenden Prozesse erkennen nicht, ob ihre letzten
Nachrichten empfangen wurden oder nicht.
Sockets und Prozesse, Threads
„
„
„
Das Empfangen per receive blockiert einen Prozess.
Damit würde ein Server-Prozess blockiert und könnte keine weiteren
Anfragen bearbeiten: sog. Sequentieller Server.
Delegiert man die Bearbeitung einzelner eingehender Verbindungen an
Threads, dann kann man weitere Anfragen auch während des Wartens
bearbeiten: paralleler Server.
47
Verbindungsorientierte Sockets in Java: Übersicht
Server
ServerSocket:
accept
Socket
InputStream
OutputStream
Client
Socket
OutputStream
InputStream
48
Verbindungsorientierte Sockets in Java
„
„
„
„
„
„
In Java ist der Zugriff auf verbindungsorientierte Sockets mit
den Klassen Socket und ServerSocket gekapselt.
Der Programmierer ist von den Niederungen spezieller
Implementierungen auf den verschiedenen Architekturen
völlig entlastet.
Die Anlage einer Instanz der Klasse ServerSocket beinhaltet
alle Funktionen zum Anlegen eines Server-Sockets.
accept liefert dann einen Socket für eine am Server
eingehende Verbindung, die ein Client durch Anlage eines
Sockets eröffnet hat.
Die Verbindung liefert Streams zum Schreiben bzw. zum
Lesen.
Daten sollten mit DataInputStream gelesen bzw. mit
DataOutputStream geschrieben werden.
49
Aufbau einer Client-Anwendung (TCP)
try {
Socket client = new Socket (Host, PortNummer);
InputStream in = client.getInputStream ();
OutputStream out = client.getOutputStream ();
// Lesen von in: ggfs Reader benutzen
// Schreiben nach out. ggfs. PrintWriter benutzen
.......
// Schließen der Streams und der Verbindung
} catch (IOException ioe) {
// Fehlerbehandlung
}
50
Aufbau eines sequenziellen Servers (TCP)
try {
ServerSocket server = new ServerSocket (PortNummer);
while (true) {
Socket verbindung = server.accept ();
InputStream in = verbindung.getInputStream ();
OutputStream out = verbindung.getOutputStream ();
// Lesen von in und Schreiben nach out
.......
// Schließen der Streams und der Verbindung
// Herunterfahren des Servers ? -> Schleife verlassen
}
} catch (Exception e) {
// Fehlerbehandlung für
// IOException, SecurityException,
// SocketTimeoutException, IllegalBlockingModeException
}
// Schließen des Servers
51
Aufbau einer parallelen Servers (TCP)
„
„
„
„
„
Eine parallele Server-Anwendung unterscheidet sich von einer
sequenziellen Anwendung dadurch, dass bei jeder mit accept()
angenommenen Verbindung ein Thread zu deren Bearbeitung gestartet
wird.
Der Server bearbeitet die Verbindung nicht mehr weiter, sondern horcht
wieder am Socket, ob weitere Verbindungen eingehen.
Der Serverbetrieb muss dann – wie beim sequenziellen Server – am Server
durch Abbruch des Java-Programms oder von Clients durch spezielle
Kommandos beendet werden.
Parallele Server sind in Java mit einem gegenüber sequenziellen Servern
kaum erhöhten Aufwand zu realisieren.
Die Software-Struktur gewinnt durch die Trennung zwischen dem Aufbau
der Verbindung und der Bearbeitung der Verbindung an Transparenz.
52
Parallele Server in Java Teil 1
class ServerThread extends Thread {
Socket verbindung = null;
ServerThread (Socket verbindung) {
this.verbindung = verbindung;
}
void run () {
try {
InputStream in = verbindung.getInputStream ();
OutputStream out = verbindung.getOutputStream ();
// Lesen von in und Schreiben nach out
..
.....
// Schließen der Streams und der Verbindung
} catch (IOException ioe) {
// Fehlerbehandlung
}
}
}
53
Parallele Server in Java Teil 2
// Server-Klasse
class Server {
// ... irgendeine Methode fuer die Arbeit im Server
ServerSocket server = new ServerSocket (PortNummer);
Socket verbindung = null;
while (true) {
try {
verbindung = server.accept ();
} catch (Exception e) {
// IOException, SecurityException,
// SocketTimeoutException, IllegalBlockingModeException
}
ServerThread serverthread =
new ServerThread (verbindung);
serverthread.start ();
// Herunterfahren des Servers ? -> Schleife verlassen
}
// Schließen des Servers
54
System-unabhängige Datendarstellung in Java
„
„
Hierzu dienen in Java die Klassen DataInputStream und
DataOutputStream.
Diese Klassen enthalten Methoden zum Lesen und Schreiben von Daten
der elementaren Datentypen
„
„
„
„
Strings können in einem modifizierten UTF-8-Format gelesen bzw.
geschrieben werden.
„
„
„
boolean, byte, char, short, int, long, float, double
readBoolean, readByte, readChar, readShort, readInt, readLong, readFloat,
readDouble
writeBoolean, writeByte, writeChar, writeShort, writeInt, writeLong, writeFloat,
writeDouble
readUTF
writeUTF
Zur Serialisierung von Objekten kann man auch die Methoden writeObject
von ObjectOutputStream bzw. readObject von ObjectInputStream
benutzen.
55
Die Socket-Pipeline
writeInt
…
Sender
readInt
…
Internet
Dataoutput
-Stream
Socket
Datainput
-Stream
Empfänger
56
Anbindung der Data…- Streams an TCP
„
Grundsätzliche Konstruktion in Java
„
„
„
„
„
InputStream is = … Irgendein InputStream
OutputStream os = … Irgendein OutputStream
DataInputStream dis = new DataInputStream(is);
DataOutputStream dis = new DataOutputStream(os);
Der Input- bzw. OutputStream kann bei verbindungsorientierten Sockets aus dem Socket ausgelesen werden:
„
Client (TCP)
„
„
„
InputStream in = client.getInputStream ();
OutputStream out = client.getOutputStream ();
Server (TCP)
„
„
„
Socket verbindung = server.accept ();
InputStream in = verbindung.getInputStream ();
OutputStream out = verbindung.getOutputStream ();
57
Anbindung der Data…- Streams an UDP
„
„
„
Problem: Datagramme (UDP) senden bzw. empfangen nur Byte-Puffer.
Diese Byte-Puffer müssen an Streams angebunden werden.
Schreiber-Lösung in Java mit ByteArrayOutputStream
„
ByteArrayOutputStream bos = new ByteArrayOutputStream();
DataOutputStream
dos = new DataOutputStream(bos);
dos.writeInt(i);
dos.writeInt(j);
„
// Die Daten in ein Byte-Array auslesen
„
„
„
„
„
„
byte[] buf = bos.toByteArray();
… Damit kann das „DatagramPacket“ geschnürt werden
Leser-Lösung in Java mit ByteArrayInputStream
// Daten als Byte-Array aus dem Paket holen
„
„
„
„
byte[] data = packet.getData();
ByteArrayInputStream bis = new ByteArrayInputStream(data);
DataInputStream
dis = new DataInputStream(bis);
int x3 = dis.readInt();
58
Beispiel: Additions-Service
„
„
Ein Server bietet die Addition ganzer Zahlen als Dienst an.
Die Schnittstelle ICalc beschreibt die Aufgabe, CalcImpl implementiert den
Dienst.
public interface ICalc {
int add(int i, int j);
}
public class CalcImpl implements ICalc {
public int add(int i, int j) {
return i + j;
}
}
„
Aufgabe:
„
„
Implementierung einer Anbindung für Clients, um Aufträge an den Server
abzusetzen. Die Ergebnisse sollen entgegengenommen und an den Client
zurückgegeben werden.
Implementierung einer Anbindung für den Server, um Aufträge
entgegenzunehmen, die Parameter zu auszupacken, den „eigentlichen“ Server
(s. o.) aufzurufen, die Ergebnisse zu verpacken und zurückzusenden.
59
Architektur der Lösung
„
„
Die Lösung erfolgt für Sockets mit TCP bzw. UDP
Proxy
„
„
„
„
„
Server
„
„
„
lokaler Stellvertreter.
Parameter auf den Kommunikationskanal schreiben.
Server aufrufen.
Ergebnisse lesen und an den Auftraggeber zurückliefern.
Der Server wartet am Socket auf Aufträge.
Für jeden Auftrag wird ein ServerThread gestartet.
ServerThread
„
„
Eingehende Daten übernehmen.
Über den Adapter die Implementierung aufrufen.
„
„
Æ Der Adapter passt die Implementierung an Streams an.
Ergebnisse zurück an den Auftraggeber (Æ Proxy) senden.
60
Grobstruktur der Lösung
Client
1
Server
8
2: Stream bzw. Paket
ServerThread
Proxy
7: Stream bzw. Paket
3
6
CalcAdapter
•Pakete bzw. Streams enthalten binäre Daten.
•Zugriff mit DataInputStream bzw. DataOutputStream.
•CalcAdapter passt die Implementierung einer Schnittstelle
an solche Datenströme an.
4
5
CalcImpl
61
Benutzte Patterns
„
Stellvertreter oder Proxy
„
„
„
„
Adapter
„
„
„
Hier wird speziell ein Remote-Proxy benutzt.
Der lokale Stellvertreter nimmt Anfragen entgegen und leitet sie zur
Bearbeitung weiter.
http://de.wikipedia.org/wiki/Stellvertreter_%28Entwurfsmuster%29
Eine Klasse passt eine Schnittstelle x an eine andere Schnittstelle y an.
http://de.wikipedia.org/wiki/Adapter_%28Entwurfsmuster%29
Beide Patterns stammen aus dem Buch der GoF (Gang of
Four)
„
Erich Gamma, Richard Helm, Ralph Johnson, John Vlissides:
Entwurfsmuster. Elemente wiederverwendbarer objektorientierter
Software. Addison Wesley, Bonn 1996 ISBN 3-89319-950-0
62
Beispiel: Client
„
„
Ein Client kann den Dienst anfordern
Ein lokaler Stellvertreter (Proxy) implementiert den Dienst.
import middleware.*;
public class Client {
public static void main (String[] args) {
ICalc proxy = new Proxy(args[0]);
int i = Integer.parseInt(args[1]);
int j = Integer.parseInt(args[2]);
int k = proxy.add(i, j);
System.out.println(k);
}
}
63
Sockets: TCP oder UDP?
„
„
„
„
„
„
Der lokale Stellvertreter (Proxy) nimmt den Auftrag entgegen.
Er kommuniziert mit dem Server entweder über TCP oder
über UDP.
In jedem Fall müssen die Daten in einer
plattformunabhängigen Darstellung übergeben werden.
Dies besorgen die Klassen DataInputStream bzw.
DataOutPutStream aus dem Package java.io.
Dieser Teil der Arbeit ist im Server für TCP und UDP identisch
und wird von der Klasse CalcAdapter geleistet.
CalcAdapter passt die Schnittstelle ICalc an die DataInputbzw. DataOutputStreams an.
64
CalcAdapter
package middleware;
import java.io.*;
public class CalcAdapter {
public void handleRequest
(DataInputStream dis, DataOutputStream dos) {
// I/O Streams vom Socket
try {
Class c = Class.forName("CalcImpl");
middleware.ICalc server = (middleware.ICalc) c.newInstance();
int x1 = dis.readInt();
int x2 = dis.readInt();
int x3 = server.add(x1, x2);
System.out.println("x1 = " + x1 + " x2 = " + x2);
dos.writeInt(x3);
}
catch (Exception e) {
System.out.println(" " + e);
}
}
}
65
TCP: Proxy (1/2)
package middleware;
import java.io.*;
import java.net.*;
public class Proxy implements ICalc {
private Socket client = null;
private DataOutputStream dos = null;
private DataInputStream in = null;
private String host = null;
public Proxy(String host) {
this.host = host;
}
void closeAll() { ... }
66
TCP: Proxy (2/2)
public int add(int i, int j) {
try {
client = new Socket (host, port);
// Verbinde die Streams
dos = new DataOutputStream (client.getOutputStream());
dos.writeInt(i);
dos.writeInt(j);
// Auslesen der Antwort des Servers
in = new DataInputStream
(client.getInputStream());
int result = in.readInt();
closeAll();
return result;
}
catch (IOException e) {
}
return 0;
}
67
TCP: Server (1/2)
package middleware;
import java.net.*;
public class Server {
public static void main(String args[]) {
ServerSocket s = null; // Socket zum Horchen
Socket con = null; // Eingehende Verbindung
// Server-Socket an port 8011 oeffnen
try {
s = new ServerSocket(8011);
String n = s.getInetAddress().toString();
}
catch (Exception e) {
System.out.println("Exception:\n" + e);
System.exit(1); // Herunterfahren des Servers
}
68
TCP: Server (2/2)
while (true) {
// Warte auf eingehende Verbindung
try {
con = s.accept();
System.out.println("accept : " + con);
Thread thread = new middleware.ServerThread(con);
thread.start();
}
catch (Exception e) {
System.out.println("Fehler : " + e);
System.exit(1); // Abbruch des Server-Betriebs
}
}
}
}
69
TCP: ServerThread (1/2)
package middleware;
import java.io.*;
import java.net.*;
public class ServerThread extends Thread {
private Socket con = null; // Connection
private DataInputStream in = null;
private DataOutputStream out = null;
void closeAll() { ... }
70
TCP: ServerThread (2/2)
public void run() {
// I/O streams vom Socket
try {
in = new DataInputStream(con.getInputStream());
out = new DataOutputStream(con.getOutputStream());
CalcAdapter ca = new CalcAdapter();
ca.handleRequest(in, out);
}
catch (Exception e) {
System.out.println(" " + e);
}
closeAll();
}
public ServerThread(Socket con) {
this.con = con;
}
}
71
Kurzinfo zu der Klasse Socket
Socket(String host, int port)
throws IOException,
UnknownHostException;
Erzeugt einen Stream-Socket und verbindet ihn zum
angegebenen Port auf dem angegebenen Host.
Socket(InetAddress address,
int port) throws IOException;
Erzeugt einen Stream-Socket und verbindet ihn zum
angegebenen Port auf der angegeben Adresse.
void close() throws
IOException;
Schließt den Socket.
InputStream getInputStream()
throws IOException;
Gibt einen InputStream für diesen Socket zurück.
Dieser kann wie in Kap. 5 beschrieben bearbeitet
werden.
InetAddress getLocalAddress();
Liefert die lokale Adresse, an die der Socket gebunden ist.
OutputStream getOutputStream()
throws IOException;
Gibt einen OutputStream für diesen Socket zurück.
Dieser kann wie in Kap. 5 beschrieben bearbeitet
werden.
boolean isClosed();
genau dann wahr, falls Socket geschlossen.
boolean isConnected();
genau dann wahr, falls Socket verbunden.
72
Kurzinfo zu der Klasse ServerSocket
ServerSocket()
throws IOException;
Erzeugt einen ungebundenen Server-Socket.
ServerSocket(int port)
throws IOException;
Erzeugt einen Server-Socket an dem angegebenen
Port. Wenn 0 angegeben ist, wird ein beliebiger freier
Port gewählt. Auch eine SecurityException ist möglich.
Socket accept();
Horcht auf eine an diesem Socket eingehende
Verbindung und akzeptiert sie.
void close();
Schließt diesen Socket.
InetAddress getInetAddress(); Gibt die lokale Adresse des Server-Sockets zurück
boolean isBound();
Liefert den Bindungs-Status des Server-Sockets.
boolean isClosed();
genau dann wahr, falls Socket geschlossen.
73
Kurzinfo zu der Klasse InetSocketAddress
InetSocketAddress
(InetAddress addr, int port);
Erzeugt eine Socket-Adresse aus ei-ner IP-Adresse
und einer Port-Nummer.
InetSocketAddress
(String hostname, int port)
Erzeugt eine Socket-Adresse aus einem Hostnamen
und einer Port-Nummer.
InetAddress getAddress();
Liefert die Internet-Adresse.
String getHostName();
Liefert den Hostnamen.
int getPort();
Liefert die Port-Nummer.
74
Kurzinfo zu der Klasse URL
URL(String protocol, String
host, int port, String file)
throws MalformedURLException;
Erzeugt eine URL aus dem Protokoll, Host, Port und
Datei.
URL(String spec) throws
MalformedURLException;
Erzeugt eine URL aus einer URL-spezifikation in
Textform.
String getQuery();
Liefert den Abfrageanteil einer URL (d.h. die Zeichenfolge nach dem Fragegezeichen ‚?’).
String getPath();
Liefert den Pfad einer URL.
int getPort();
Liefert den Port einer URL.
String getProtocol();
Liefert das Protokoll einer URL.
String getHost();
Liefert den Host einer URL. Das Format entspricht RFC
2732, d.h. für eine IPv6-Adresse wird die Adresse in
Klammern ('[' und ']') geliefert.
String getFile();.
Liefert den Dateinamen dieser URL
InputStream openStream()
throws IOException;
Liefert einen Eingabestrom, um von der URL zu lesen.
75
Herunterladen