Die Präsentation wird geladen. Bitte warten

Die Präsentation wird geladen. Bitte warten

Client-Server Kommunikation

Ähnliche Präsentationen


Präsentation zum Thema: "Client-Server Kommunikation"—  Präsentation transkript:

1 Client-Server Kommunikation
Die Socket Schnittstelle Clemens Düpmeier,

2 Kommunikation über Socket API
Rechner kommunizieren heutzutage hauptsächlich über TCP/IP Netze (Inter-/Intranets) Die in C geschriebene Socket-API ist die (unterste) Betriebssystemschnittstelle zur Kommunikation über TCP/IP Netze Sockets sind in Analogie zum Umgang mit Dateidatenströmen realisiert Die Socket Schnittstelle stellt die unterste Ebene der für Applikationsprogrammierer gedachten Betriebssystemschnittstellen zur Kommunikation zwischen Rechnern dar. Sie wurde als Protokoll- und Netzwerksoftware übergreifende Schnittstelle konzipiert und unterstützt neben den Internet Protokollen auch andere Protokolle, wie Decnet oder IPX. Die Internetprotokolle haben sich allerdings im Laufe der Zeit zu dem defacto einzigen noch ausgiebig verwendeten Netzstandard entwickelt, so dass die Socket API fast ausschließlich noch für Internetkommunikation verwendet wird (Es gibt aber z.B. auch noch Multiplayerspiele, die IPX einsetzen). Die Socket API ist in C geschrieben und auf allen Betriebssystemen lauffähig. Bei Windows Systemen wird sie durch die winsock* DLL‘s bereitgestellt. Alle höheren Programmierschnittstellen, wie RPC, CORBA, DCOM usw. basieren auf der Socket API. Die Socket API wird in verschiedenen anderen Sprachen, wie Java usw. auch in Form vereinfachter, je nach Sprache objektorientierter Form angeboten. Die Socket API definiert einen Socket als Konstrukt, das in einem Prozess einen Kommunikationsendpunkt darstellt. Sockets sind dabei in Analogie zu Filedeskriptoren konzipiert und auch auf diese Weise in das Betriebssystem integriert. So können z.B. die normalen read() und write() Aufrufe und darauf aufsetzende Bibliotheksfunktionen, wie fprintf über Sockets verwendet werden. Clemens Düpmeier,

3 Wichtige Funktionen der Socket API
socket Erzeugen eines Sockets bind Binden einer Adresse an einen Socket accept - Verbindungswunsch akzeptieren connect - Verbindung anfordern read - Lesen vom Socket write - Schreiben auf Socket Mit dem socket() Aufruf wird zunächst ein Kommunikationsendpunkt (Socket) erzeugt. Der Rückgabewert ist ein „normaler“ Filedeskriptor. Der Server kann mit bind() dem Socket eine feste Adresse (IP Adresse + Portnummer) zuordnen. Mit accept() wartet der Server auf einen Verbindungswunsch. Der Client fordert eine Verbindung durch Aufruf von connect() an. Die Funktionen read() und write() dienen dann zum Lesen und Schreiben von bzw. auf den Socket. Clemens Düpmeier,

4 Server-Client Kommunikation
Server (passiv) Client (aktiv) s=socket(...); bind(/* address */); listen(s,length); for (;;) { new_sock=accept(/*...*/); if (fork() == 0) handle(new_sock); } s=socket(...); /* no bind */ connect(s, /* address */); talk_with_server(s); Sowohl Client wie auch Server müssen zunächst einmal über den socket() Aufruf einen Kommunikationsendpunkt (Socket) innerhalb ihres Codes definieren. Der Server ordnet seinem Socket dann eine feste Adresse (IP-Adresse + Portnummer) durch einen bind() Aufruf zu. Weiter stellt der Server über den Aufruf von listen() die Länge seiner Listen-Warteschlange ein. In dieser Warteschlange werden Verbindungswünsche eingereiht, die schon hereingekommen sind, vom Serverprozess aber noch nicht innerhalb eines accept() Aufrufes angenommen wurden. Die Hauptaufgabe des Serverrumpfes ist es dann, hereinkommende Verbindungswünsche in accept() Aufrufen entgegenzunehmen und zu behandeln oder an einen neu erzeugten Prozess oder Thread zur weiteren Behandlung weiterzuleiten. Das Ergebnis eines accept() Aufrufes bei Zustandekommen einer Verbindung ist stets eine neuer Socket (new_sock) als Returnwert des accept() Aufrufes oder sonst eine Fehlerindikation. Über diesen neuen Socket muss dann der weitere Kommunikationsverkehr mit dem Client laufen. Der Client kann sich bind(), listen() und accept() sparen. Er verwendet stattdessen den connect() Aufruf, um sich aktiv mit dem Server zu verbinden. Dabei wird seinem Socket intern eine beliebige, freie Portnummer zugewiesen. Der Client kommuniziert nach einem erfolgreichen connect() Aufruf mit dem Server über den gleichen Socket, den er auch für den Verbindungsaufbau benutzt hat. Clemens Düpmeier,

5 Funktionsweise von accept()
Serverrechner Clientrechner Serverprozess Clientprozess Sockets Socket accept nach accept Das Bild verdeutlicht die Zuordnung von Sockets und Portnummern (d.h. Schnittstellen zu den unteren Netzprotokollen / Paketen) bei einer verbindungsorientierten Kommunikation zwischen Client und Server. Während beim Client der gesamte Kommunikationsverkehr über einen Socket erfolgt, dem beim connect() eine willkürliche, freie Portnummer (im Bild 50167) zugeordnet wird, arbeitet die Serverseite mit einem festen Socket, der innerhalb der accept() Aufrufe verwendet wird und temporär neu erzeugten Sockets, die man als Returnwert von gelungenen accept() Aufrufen erhält. Der eigentliche Datenaustausch mit dem Client erfolgt immer über die temporär innerhalb der accept() Aufrufe erzeugten Sockets. Der vom Servercode selbst erzeugte Socket wird nur für die accept() Aufrufe verwendet. Alle temporär erzeugten Sockets sind jedoch mit der gleichen Portnummer assoziiert, wie sie beim bind() Aufruf mit dem festen Socket festgelegt wurde. bind() liefert natürlich eine Fehlerindikation, wenn die gewünschte IP-Adresse, Portnummer Kombination schon durch einen anderen Socket (z.B. in einem anderen Prozess) belegt ist. Fehlerindikationen: accept(), socket(): liefert Fehler, wenn es aus Ressourcegründen oder ähnliches kein neuer Socket mehre angelegt werden kann connect() liefert Fehler, wenn keine Verbindung zustande kommt (Serverprozess nicht vorhanden, oder Rechner nicht im Netz) bind() liefert Fehler, wenn Adresse schon benutzt wird (anderer Prozess hat Port in Benutzung) read()/write() liefern Fehler, wenn Verbindung unterbrochen wird (falls z.B. einer der Partner die Verbindung schließt). Die Portnummer ist (wie bereits in der Einleitung erörtert eine Eigenschaft des TCP bzw. UDP Protokolles. Dabei sind UDP Ports disjunkt von TCP Ports, d.h. UDP Portnummern können unabhängig von TCP Portnummern verwendet und benutzt werden. 80 50167 Netzwerkschnittstelle Netzwerkschnittstelle LAN Clemens Düpmeier,

6 Eigenschaften der Socket Schnittstelle
Unterste Schnittstelle zum Betriebssystem Synchronisation der Kommunikation liegt voll beim Anwender Fehlerbehandlung liegt ebenfalls beim Anwender Nicht binär transparente Datenübertragung Nicht objekt-orientiert Die socket() API ist ein Bestandteil der Betriebssystem API und stellt die unterste (direkt durch Anwendungsprogrammierer nutzbare) Schnittstelle des Betriebssystems zum Netzwerk dar. Darunter befinden sich lediglich interne Betriebssystem API‘s (z.B. des mbuf Puffermanagement für Pakete) oder die Treibersoftware für die Netzwerkhardware. Problematisch bei der Socket API ist, dass die Synchronisation und Fehlerbehandlung voll beim Anwendungsprogrammierer liegt. Dieses Problem sollte man nicht unterschätzen. Die Basisfunktionalität eines die socket() API nutzenden Programmsystems ist schnell geschrieben. Dieses dann allerdings dazu zu bringen, das es sich auch in allen Anwendungssituationen, wie z.B. vorübergehender Ausfall des Netzwerkes oder andere Kommunikationsprobleme, wie eine zu langsame Verbindung, vernünftig verhält ist eine Kunst für sich, die einiges an Programmiererfahrung im Bereich Netzwerkprogrammierung voraussetzt. Ein weiteres großes Problem der Socket API ist, dass sie überhaupt nicht transparent bzgl. der Übertragung binärer Daten ist. Die Probleme hierbei beginnen schon, wenn man IP-Adresse und Portnummer zu Fuß angeben will und dabei nicht beachtet, dass Maschinen unterschiedliche Byte-Ordering Anordnungen haben. Bei direkter Angabe z.B. der Portnummer muss man Hilfsfunktionen der Socket API (wie htons() = Host to Network Short) verwenden, um die anzugebende Portnummer als Short erst in ein Network Byte Ordering zu bringen. Auch in read() und write() Aufrufen kann man über die Socket API mehr-bytige binäre Datenstrukturen nicht transparent übertragen ohne irgendein normiertes Codierungs- und Decodierungsverfahren zu verwenden. Aus diesem Grunde verwenden viele Anwendungen, die direkt auf Sockets aufsetzen (wie HTTP oder SMTP), eine Übertragung von Daten in Form von ASCII Nachrichten. Dies behebt das Problem der Nichttransparenz bzgl. der binären Daten und hat weiter den Vorteil, das der Kommunikationsverkehr zwischen Client und Server auch durch den Menschen selbst lesbar ist, was z.B. das Debuggen durch die Programmierer erleichtert. Nachteil dieser Methode ist, das ASCII Codierung sehr ineffizient ist und stets binäre Werte erst durch Interpretation der ASCII Texte erst wieder zurückgewonnen werden müssen, z.B. mit sscanf() oder ähnlichen Funktionen. Clemens Düpmeier,

7 SMTP Protokoll $telnet mailhost.iai.fzk.de 25 Trying 141.52.44.1...
Connected to mailhost.iai.fzk.de Escape character is '^]'. 220 mailhost.iai.fzk.de sendmail version 10.1 ready at Wed, 6 May :52: HELO uisun7.iai.fzk.de 250 mailhost.iai.fzk.de Hello uisun7.iai.fzk.de MAIL From: 250 Sender OK (verified) RCPT To: <duepmeier> 250 Receiver OK (verified) DATA 354 Start mail input; end with <CRLF>.<CRLF> Subject: Dann wollen wir mal SMTP testen Hier beginnt die eigentliche Nachricht der Mail Dann können auch noch weitere Zeilen folgen, die allerdings mit einem Punkt auf einer einzelnen Zeile enden müssen. . 250 OK QUIT Die Folie verdeutlicht das SMTP Protokoll. Generell braucht man zur Kommunikation über Sockets ein Anwendungsprotokoll, dass die Reihenfolge der Datenaustauschaktionen zwischen den Kommunikationspartnern regelt. Solche Anwendungsprotokolle gibt es im Internet viele und sie werden in Form der RFC (Request for Comments) aufgeschrieben, die dann als Standardisierungspapiere der Protokolle dienen. Da Sockets nicht transparent bzgl. binärer Datenübertragung sind, sind viele Internetanwendungsprotokolle ASCII bzw. text-basiert, d.h. es werden nur Nachrichten im ASCII Code oder einem 8-Bit Code übertragen, der unabhängig von Maschinenarchitekturen ist. SMTP (Simple Mail Transport Protokoll) ist eines dieser ASCII basierten Protokolle. Der Client schickt hier ASCII und zeilenbasierte Kommandos an den Server, die dieser mit Anworten in der Struktur Fehlercode OK (oder ERROR) weiterer Text zum Lesen quittiert. Kommandos, die der Client senden kann sind (wie oben benutzt) HELO - Anmelden und Grüßen des Servers MAIL From: mail-addresse - Angabe des Absenders der Mail RCPT To: mail-addresse - Angabe des Empfängers der Mail DATA - Eingabe des Mailrumpfes QUIT - Abmelden vom Server Das SMTP Verfahren Mail zu senden, wurde durch die MIME (Multimedia Internet Mail Extension) Standardisierung dahin verbessert, dass im Rumpf beliebige (auch binäre ) Attachments encodiert werden können. Die binären Attachment werden dabei mit einem Codierungsverfahren in ASCII codiert (z.B. uuencoding oder base64 Encoding). MIME legt weiter fest, wie verschiedene Teile einer im Rumpf voneinander getrennt werden und wie der Inhaltstyp eines Teiles festgelegt wird (Das ist die bekannte MIME Inhaltskennung, also z.B. text/html oder text/plain oder image/jpg). Clemens Düpmeier,

8 HTTP Protokoll - Ablauf
Anfrage Client Server GET /index.html HTTP/1.0 If-Modified-Since: Saturday, 12-Dec-98, 12:34:56 GMT Antwort HTTP/ OK MIME-Version: 1.0 Content-type: text/html Content-length: 123 Last-Modified: Saturday, 12-Dec-98 12:34:56 GMT <HTML>Hier beginnt die eigentliche HTML Datei... Diese Folie zeigt den prinzipiellen Ablauf beim HTTP Protokoll. Der Client führt hier eine Anfrage aus, die aus einer Kommandozeile (hier mit dem Kommando GET) besteht, der optional weitere Headerzeilen der Gestalt Header-Name: Wert und durch eine Leerzeile getrennt eventuell (bei Kommandos wie POST) auch ein Inhaltsteil folgen kann (siehe auch Antwort Server). Der Server liefert als Antwort zunächst eine Statuszeile (HTTP/ OK), die das verwendete Protokoll, den Fehlercode (hier 200) und eine textuelle Statusmitteilung (hier OK) enthält. Dann folgen mehrere Headerzeilen mit weiteren Metainformationen, wie Inhaltstyp, Länge des Inhalts, Datum der letzten Modifikation des Dokumentes). Durch eine Leerzeile getrennt folgt dann der Inhalt des gewünschten Dokumentes. Clemens Düpmeier,

9 Einige HTTP Clientkommandos
GET Liefert ein Datei vom Server HEAD Liefert nur Dateiinformationen POST Sendet Daten (CGI) an den Server PUT Sendet komplette Dateien an Server DELETE Löschte Datei vom Server Clemens Düpmeier,

10 Beispiele für HTTP Requests
GET / HTTP/1.0 Connection: Keep-Alive User-Agent: Mozilla/2.0 (Win95; I) Host: merlin Accept: image/gif, image/x-xbitmap, image/jpeg, image/pjpeg, */* POST /cgi/4848 HTTP/1.0 Referer: Connection: Keep-Alive User-Agent: Mozilla/3.01 (X11; I; SunOS 5.4 sun4m) Host: tecfa.unige.ch:7778 Accept: image/gif, image/x-xbitmap, image/jpeg, image/pjpeg, */* Content-type: application/x-www-form-urlencoded Content-length: 42 name=Ulli&nachname=Ullenboom Clemens Düpmeier,

11 HTTP Server Statuscodes
1xx: Informierend, 2xx: Erfolgreich 3xx: Rückfrage, 4xx: Fehler beim Client 5xx: Fehler beim Server 200 OK 201 Created Accepted 204 No Content 300 Multiple Choices 301 Moved Permantly 302 Moved Temporarily 304 Not Modified 400 Bad Request 401 Unauthorized 403 Forbidden 404 Not Found 500 Internal Server E. 501 Not Implemented 502 Bad Gateway 503 Service Unavailable Clemens Düpmeier,

12 Java Socket API Die Java Socket API lässt sich viel einfacher bedienen als die C API. Dies liegt daran, dass sie ausschließlich auf die Internetprotokolle zugeschnitten ist und Objektschnittstellen hier die komplizierten Details der C Socket API, wie die komplexe Angabe der Adresse und die nichtportable binäre Form der Adresse kapseln. Clemens Düpmeier,

13 InetAddress Klasse InetAddress
static IntetAddress getByName() // erzeuge Adressobjekt static InetAddress getLocalHost() // erzeuge Adressobjekt Byte[] getAddress() String getHostName() // gebe mir Hostname String getHostAddress() // gebe mir Adresse Die InetAddress Klasse enkapsuliert in Java Internet Hostaddressen ( ), also IP-Adressen. Sie besitzt statische Methoden (z.B. getByName() und getLocalHost()), um solche Adressobjekte zu erzeugen und nicht-statische Methoden, um sich verschiedene Repräsentationsformen der Adresse von einem Adressobjekt zurückgeben zu lassen. Achtung: In getByName() kann man Hostnamen, also mailhost.iai.fzk.de, aber auch Adressen in Dotted-Form also als String angeben, um ein zugehöriges Adressobjekt zu erhalten. getHostName() liefert den Hostnamen (übrigens nicht unbedingt in kanonischer, also Langform mit allen Domain: hiefür gibt es eine weitere Methode) getHostName als String die IP-Adresse in Dotted-Form und getAddress() liefert die IP-Adresse als Array von 4-Bytewerten. Clemens Düpmeier,

14 Beispiel Nutzung von InetAddress
public class WhoAmI { public static void main(String args[]) throws Exception { InetAddress myAddress=InetAddress.getLocalHost(); System.out.println("Hostname: " myAddress.getHostName()); System.out.println("IP Adresse: " myAddress.getHostAddress()); } Das Programm gibt den Hostnamen und die IP Adresse des lokalen Hosts aus. Achtung: in den Netzwerkprogrammen fehlt der typische Code zur Behandlung der Ausnahmesituationen, die Fehler bei der Kommunikation abfangen. Clemens Düpmeier,

15 InetSocketAddress Klasse
InetSocketAddress (extends SocketAddress) InetSocketAddress(String hostname, int port) InetSocketAddress(InetAddress address, int port) InetAddress getAddress() // Hostteil als InetAdress getHostName(): String // gebe mir Hostname getPort(): String // gebe mir Portnummer Die InetSocketAddress Klasse enkapsuliert in Java Adressen, die Sockets zugeordnet werden, d.h. sie enthalten sowohl IP-Adressangaben als auch zugehörige Ports. Angelegt werden Sie über Konstruktoren, bei denen am den Hostnamen entweder als String oder bereits als ein InetAdress und die Portnummer als normale Integer angibt. Weiter gibt es einen Konstruktor für Wildcard IP-Adressen, d.h. bei Erzeugen einer lokalen Adressen gibt man keine IP-Adresse an und dies heisst, dass ein accept() auf allen Interface (falls also der Rechner mehr als eine IP-Adresse hat) erfolgen soll. Die Klasse besitzt weiter Methoden zur Abfrage der einzelnen Teile eines InetSocketAddress Objektes (also Portnummer, InetAddress und/oder Hostname). Clemens Düpmeier,

16 Client-Server Socketverbindungen in Java
Server Applikation Client Anwendung ServerSocket blockiert in accept() bis Client einen Verbindungsaufbau durchgeführt hat new ServerSocket Objekt new Socket Objekt accept() connect() Das Diagramm zeigt den zeitlichen Ablauf innerhalb einer TCP, also verbindungsorientierten Client-Server Applikation unter Nutzung von Java. Der Server erzeugt hierbei einen ServerSocket (Objekt von der Klasse ServerSocket) und wartet duch Aufruf der accept() Methode auf das Hereinkommen von Verbindungswünschen. Der Client erzeugt ein Socket Objekt (Klasse Socket) und ruft dessen Methode connect() auf. Auf der Serverseite führt ein Verbindungswunsch des Clients zum Erzeugen eines serverseitigen Socket Objektes (Klasse Socket), das nun mit dem clientseitigen Socketobjekt verbunden ist. Server und Client kommunizieren nun über die beiden Socket Objekte (also: der Server benutzt zur Datenkommunikation mit dem Client nicht das ServerSocket sondern das in accept zurückgegebene Socket Objekt!). return Socket Objekt Die beiden Sockets sind nun verbunden Datenaustausch über das Netz Clemens Düpmeier,

17 Socket Klasse Socket Socket() Socket(String host, int port)
Socket(InetAddress host, int port) ... void close() void connect(SocketAddress endpoint) Void connect(SocketAddress endpoint, int timeout) int getPort() InetAddress getInetAddress() InputStream getInputStream() OutputStream getOutputStream() Erzeugt Socket als Endpunkt der Clientseite einer TCP basierten Client-Server Kommunikation. Falls Portnummer und IP Adresse oder Hostname Angegeben werden (im Konstruktor), wird ein Connect zum jeweiligen Host durchgeführt. Ansonsten kann man ein connect auch später mit der Methode connect durchführen, wobei man ein InetSocketAdress Objekt, das Host und Port Beinhaltet, beim connect angibt. close() schließt den Socket wieder. Mit getPort() und getInetAdress() kann man abfragen, mit welchen Port bzw. mit welchem Host ist der Socket verbunden. getInputStream() und getOutputStream() liefern mir InputStream und OutputStream zur Kommunikation mit dem Server (InputStream zum Lesen vom Server, OutputStream zum Schreiben in Richtung Server). Clemens Düpmeier,

18 ServerSocket Klasse ServerSocket ServerSocket() ServerSocket(int port)
ServerSocket(int port, int backlog) ... void bind(SocketAddress endpoint) Void bind(SocketAddress endpoint, int backlog Socket accept() void close() int getLocalPort() InetAddress getInetAddress() Erzeugt ein ServerSocket Objekt, das die Serverseite einer TCP Client-Server Applikation darstellt. Je nach Konstruktor wird der Socket an einen beliebigen freien Port oder an einen vorgebenen Port gebunden. Über weitere Konstruktor kann man auch die (lokalen) IP-Adressen angeben, an denen der Server auf Clients wartet (sofern man mehrere lokale IP Adressen hat). Weiter kann man auch die Größe der listen Warteschlange (Parameter backlog) mit bei der Erzeugung des Sockets angeben. Ein bind kann man auch nachträglich mit der bind Methode durchführen. Typischer Weise verwendet man nach der Erzeugung eines Server Sockets die Methode accept(), um auf hereinkommende Verbindungswünsche zu warten. Kommt eine Verbindung zustande, gibt accept ein Socket Objekt zurück, über das man dann (siehe vorherige Folie) mit den Client kommunizieren kann. Die Methoden getInetAddress() und getLocalPort() geben beim ServerSocket die lokalen Addressgrößen zurück. Die Adresse des Clients nach einem accept() erhält man über das bei accept() zurückgegebene Socket Objekt. Clemens Düpmeier,

19 Beispiel TCP Serverapplikation
public class MultServer { public static void main(String args[]) throws Exception { ServerSocket server=new ServerSocket(3141); while (true) { Socket client=server.accept(); InputStream in =client.getInputStream(); OutputStream out=client.getOutputStream(); int firstNumber=in.read(); int secondNumber=in.read(); out.write(firstNumber * secondNumber); } } Simpler Server, der zwei kleine Zahlen (<< gegen 255) nimmt, diese multipliziert und das Ergebnis an den Client zurückgibt. Typische Struktur eines Servers mit kurzer Bearbeitungszeit der Clientanforderung: Server erzeugt zunächst Serversocket. Dann wartet er in einer Endlosschleife in Aufruf der accept() Methode des Servers auf hereinkommende Clientverbindungen. accept() gibt ein Socket Objekt zurück, über das die Kommunikation mit dem Client abgewickelt wird. Zum eigentlichen Datenaustausch benötigt man ein InputStream bzw. OutputStream Objekt, das mit dem Socket assoziert ist, über das die eigentlichen Daten geschickt werden. Diese erhält man über Aufruf von client.getInputStream() bzw. client.getOutputStream(). Achtung: InputStream bzw. OutputStream stellen nur byteweise Lese bzw. Schreibmethoden zur Verfügung. Da wir mit read() nur ein Byte lesen und mit mit write() nur ein Byte schreiben, funktioniert dieser Multiplikationsserver nur mit Zahlen, die aus einem Byte bestehen und deren Multiplikation auch in einem Byte untergebracht werden kann. Clemens Düpmeier,

20 Beispiel TCP Clientapplikation
public class MultClient { public static void main(String args[]) { try { Socket server = new Socket("Hostname", 3141); InputStream in = server.getInputStream(); OutputStream out = server.getOutputStream(); out.write(4); out.write(5); int result=in.read(); System.out.println("4 * 5 = " +result); } catch (IOException e) { System.out.println("Fehler bei Kommunikation"); } } Typischer Aufbau einer TCP Clientapplikation unter Java. Diese erzeugt ein Socket Objekt server (oft mit gleichzeitigem connect, wie hier) und besorgt sich von diesem Socketobjekt die zugehörigen InputStream und OutputStream (getInputStream() und getOutputStream()) Objekte. Für diese gelten die gleichen Bemerkungen wie beim Serverbeispiel, d.h. es ist über die Streamobjekte direkt nur byteweise Kommunikation möglich. Clemens Düpmeier,

21 Gepufferte Ein-/Ausgabe über Sockets
Socket t = new Socket(...); BufferedReader in = new BufferedReader( new InputStreamReader(t.getInputStream())); PrintStream os = new PrintStream(t.getOutputStream()); ... in.readLine(); os.println("Irgendwelche Strings + Objekte mit toString() ausgeben"; Zur zeilenorientierten Kommunikation zwischen Client und Server, wie dies bei vielen Anwendungsprotokollen, wie SMTP, der Fall ist, benutzt man am geeignesten BufferedReader Objekte für die Eingabe bzw. PrintStream Objekte für die Ausgabe. Der obige Code zeigt, wie man diese aus den InputStream bzw. OutputStream Objekten des Sockets erhält. BufferedReader stellen eine readLine() Methode bereit, mit der man Zeilen lesen kann. PrintStream Objekte haben viele Methoden, mit der man Basistypen und ausgeben kann und eine println Methode für die zeilenweise Ausgabe von Strings. Da die meisten Objekte eine toString() Methode haben, die automatisch diese Objekte in Strings codiert, kann man hiermit viele Objekte in String- und zeilenbasierter Form über einen Stream schicken. Die InputStreamReader Klasse erzeugt ein Zischenobjekt, das die Bytefolge des InputStream zunächst in eine Folge von Zeichen (Character) konvertiert. Durch ein weiteres Argument im Konstruktor dieser Klasse könnten wir die spezielle Zeichensatzcodierung, die wir haben wollten, codieren, wenn uns der Default nicht reichen würde. Clemens Düpmeier,

22 Client mit zeilenorientierter Kommunikation
public class PingClient { public static void main(String args[]) throws Exception { Socket t = new Socket(args[0], 7); BufferedReader in = new BufferedReader( new InputStreamReader(t.getInputStream())); PrintStream out = new PrintStream(t.getOutputStream()); String test= "Hallo aus Karlsruhe, vom " + new Date(); out.println(test); String antwort=in.readLine(); if (antwort.equals(test)) System.out.println("Server lebt"); else System.out.println("Server ist nicht erreichbar"); } Client, der beim Serverrechner (gegeben durch args[0]) beim Ping Dienst (Port 7) anklopft. Der vom Client gesendete String wird von diesem Service in identischer Weise an den Client zurückgesendet. Der Client testet, ob er diesen String zurück erhält. Clemens Düpmeier,

23 Kommunikation über UDP
dp Paket enthält neben Daten auch Ziel-IP und Zielportnummer Applikation new DatagramSocket Objekt DatagramPacket Objekt (dp) new Bei UDP orientierter Kommunikation werden Datenpakete in Form eines DatagramPacket Objekten übertragen. Die Ziel-IP und Portnummer werden dabei für gewöhnlich neben den Daten dem Paketobjekt mit übergeben und sind Bestandteil des Paketes und nicht des Socketobjektes. Datenpakete werden über ein DatagramSocket Objekt durch Aufruf der Methode send() verschickt bzw. durch Aufruf von receive() empfangen. Wie schon eingangs der Vorlesung besprochen garantiert das UDP Protokoll weder das Ankommen eines Paketes noch eine Reihenfolge eintreffender Pakete. Pakete, die ankommen, sind allerdings fehlerfrei, d.h. die Bytes, die sie enthalten, sind in Ordnung. send/receive(dp) Clemens Düpmeier,

24 DatagramSocket Klasse
DatagrammSocket(int port) DatagrmSocket(int port, InetAddress laddr) ... void bind(SocketAddress laddress) void close() void receive(DatagramPacket p) void send(DatagramPacket p) int getLocalPort() InetAddress getLocalAddress() Ein DatagramSocket Objekt ist der Ausgangspunkt für das Verschicken von Datagram Paketen (Objekte vom Typ DatagramPacket). DatagramSocket Objekte lassen mit beliebig vom System gewählter interner Adresse (IP, Portnummerkombination) erzeugen oder man kann Portnummer und optional auch IP-Nummer des Interface (oder der Interfaces) vom lokalen Host, die benutzt werden sollen (für receive) angeben. Die lokale Adresse lässt sich auch nachträglich (sofern bei Konstruktion nicht angegeben) noch mit bind() setzen. Zum Senden und Empfangen von Paketen wird receive bzw. send() verwendet. Beide erwarten als Argument ein DatagramPacket Objekt. receive füllt dieses während es bei send bereits mit Daten gefüllt sein sollte. Die Klasse enthält weiter Methoden zum Abfragen von Informationen, Setzen von Optionen des Sockets, usw. Clemens Düpmeier,

25 DatagramPacket Klasse
DatagramPacket(byte[] buf, int length) DatagrammSocket(byte[] buf, int length, InetAddress addr, int port) ... InetAddress getAddress() void getPort() byte[] getData() void setData(byte[] buf) int getLength() Diese Klasse repräsentiert Datenpakete, die über das UDP Protokoll übertragen werden. Der erste Konstruktor ist für leere Datenpakete gedacht, die als Argument in receive() Methoden der DatagramSocket Klasse eingehen. Man gibt hier einen leeren Datenpuffer und seine Länge an. Der zweite Konstruktor ist für Datenpakete gedacht, die gesendet werden sollen. Man gibt hier nicht nur den Puffer mit den Daten und dessen Länge, sondern auch die Zieladresse und Ziel-Portnummer für das Paket an. Daten lassen sich nachträglich aus dem Paket mit getData() auslesen bzw. mit setData() setzen. Weiter gibt es Möglichkeiten um die mit dem Paket assoziierte Adresse (Senderadresse) auszulesen. Clemens Düpmeier,

26 Beispiel UDPClient public class UDPClient { public static void main(String args[]) throws Exception { DatagramPacket packet; DatagramSocket dSocket = new DatagramSocket(); InetAddress serverAddress=InetAddress.getByName("ServerName"); while (true) { String s = new Date().toString(); packet = new DatagramPacket(s.getBytes(), s.length(), serverAddress, 4711); dSocket.send(packet); System.out.println("Paket " + packet + " abgeschickt"); Thread.sleep(1000); } Typischer Ablauf eines Clients, der periodisch über UDP Daten (z.B. Messdaten) an einen zentralen Server schickt. Im obigen Programm wird nur ein Datum geschickt. Typischer Weise würde diesem Datum der entsprechende Messwert noch hinzugefügt. Der Client erzeugt zunächst einen DatagramSocket mit beliebiger Adresse, besorgt sich ein InetAddress Objekt mit der IP Adresse des Servers und sendet dann in der while Schleife seine DatagramPacket Pakete. Clemens Düpmeier,

27 Beispiel UDPServer public class UDPServer { public static void main(String args[]) throws Exception { byte data[] = new byte[1024]; DatagramPacket packet; DatagramSocket socket = new DatagramSocket(4711); while (true) { packet=new DatagramPacket(data, data.length); socket.receive(packet); InetAddress address = packet.getAddress(); int port = packet.getPort(); System.out.println("Paket von " + packet.getAddress() + "am Port " + packet.getPort() + " erhalten"); insertIntoDatabase(packet.getData()); } }} Fiktives Serverprogramm zu unserem UDP Client. Der Server nimmt über Aufruf der receive() Methode an seinem DatagramSocket Objekt Pakete entgegen, protokolliert auf Standardausgabe von wo diese kommen und trägt dann die Daten mit einer weiteren Methode, die hier nicht dargestellt ist, in eine Datenbank ein. Clemens Düpmeier,

28 Vorteile Low-Level Client Server
effizient bzgl. Laufzeit überall verfügbar standardisiertes Interface für interne und externe Kommunikation verfügbar Clemens Düpmeier,

29 Nachteile Low-Level Kommunikation
zu komplex bzgl. der Kommunikationsdetails Synchronisation/Fehlerbehandlung liegt voll beim Anwendungsprogrammierer nicht binär-transparent ohne Arbeit Clients müssen wissen, wo Server sind Mix von Applikationslogik + Netzwerktransportlogik Clemens Düpmeier,

30 Client-Server Zugriff auf Datenbanken (am Beispiel von JDBC)
Clemens Düpmeier,

31 Was ist die JDBC API eigentlich Produktname, aber oft übersetzt als Java Database Connectivity API API zum Zugriff auf Datenquellen, die eine tabellen-artige Struktur haben Beispiele für Datenquellen sind: relationale Datenbanken Dateien mit tabellenartiger Struktur ODBC Datenquellen unter Windows Wie schon angedeutet erlaubt JDBC den java-seitigen Zugriff auf Datenbanken. Allgemeiner ist JDBC nicht auf Datenbanken beschränkt, sondern ist als API für den Zugriff auf beliebige tabellarische Daten gedacht. Diese können sich in relationale Datenbanken, Dateien mit tabellarischer Struktur oder z.B. auch ODBC Datenquellen befinden. Clemens Düpmeier,

32 Typisches Nutzungsschema
Connection con = DriverManager.getConnection( "jdbc:myDriver:wombat", "myLogin", "myPassword"); Statement stmt = con.createStatement(); ResultSet rs = stmt.executeQuery("SELECT a, b, c FROM Table1"); while (rs.next()) { int x = rs.getInt("a"); String s = rs.getString("b"); float f = rs.getFloat("c"); } Treiber für spezielle Datenquelle laden Verbindung mit Datenquelle herstellen Abfrage machen oder Update Operation durchführen Resultate der Abfrage bearbeiten Als erster Schritt bei der Nutzung von JDBC muss von der Anwendung ein für die zu benutzende Datenquelle(n) geeigneter Treiber (oder mehrere) geladen werden. Im obigen Nutzungsscenario wird dieses Laden des Treibers nicht gezeigt (siehe hierzu weitere Folien) wurde der Treiber geladen, so kann man mit gewünschten Datenquellen eine Verbindung über den zugehörigen Treiber aufbauen. Hierzu benutzt man die JDBC Klasse DriverManager, die statische Methode getConnection besitzt, mit der man eine Verbindung zu einer Datenbank aufbauen kann, für die ein Treiber geladen wurde. Das so gewonnene Connection Objekt erlaubt dann die Erzeugung von Statement Objekten, die als Container für Datenbankabfragen dienen. Die Statement Klasse besitzt unter anderem eine Methode executeQuery, mit der eine in SQL formulierte Abfrage der Datenbank durchgeführt werden kann, und eine Methode executeUpdate, mit der eine Update Operation innerhalb der Datenbank über ein geeignetes SQL Statement durchgeführt werden kann. Das Ergebnis einer Query ist ein ResultSet Objekt, das die Abfrageergebnisse als Liste von Zeilenobjekten kapselt. Die ResultSet Klasse hat Methoden, um die einzelnen Ergebniszeilen anzusprechen (oben z.B. rs.next()) und die Werte in den Spalten so einer Zeile zurückzugeben (rs.getInt("a"), etc.). Wir wollen uns im folgenden die verschiedenen Schritte bei der Nutzung von JDBC einmal genauer ansehen. Clemens Düpmeier,

33 Typen von JDBC Treibern
JDBC-ODBC Bridge Treiber nutzt die ODBC API, um auf ODBC Datenquellen zuzugreifen Native-API Java Treiber ruft von Java aus Native Datenbank API auf Netz-Protokoll All-Java Treiber benutzt Netz-Protokoll, um auf Middleware Server zuzugreifen, der seinerseits auf Datenbank zugreift Native Protocol All-Java Treiber direkter Aufruf des Datenbankservers über DBMS Netzprotokoll JDBC Treiber, die vor Verwendung der JDBC API geladen werden müssen, kommen in unterschiedlichen Typen vor. Diese Folie gibt eine Übersicht über die einzelnen Treiber. Heutzutage nutzt man vorzugsweise Native Protocol All-Java Treiber (Level 4 Treiber) zum Zugriff auf relationale Datenbanken. JDBC-ODBC Bridge Treiber (Typ 1 Treiber) werden benutzt, um z.B. auf Access Datenbanken unter Windows zuzugreifen. Level 4 Treiber haben in der Regel den wenigsten Overhead, benutzen das Native Datenbanknetzwerkprotokoll der Zieldatenbank und sind somit in der Regel die beste und performanteste Treiberversion. Alle bekannten relationalen Datenbankhersteller liefern frei solche Treiber für ihre Datenbank, die von ihrer Website gezogen werden können. Clemens Düpmeier,

34 JDBC-ODBC Datenquelle
try { Class.forName("sun.jdbc.odbc.JdbcOdbcDriver"); } catch (Exception e) { System.out.println("Konnte JDBC-ODBC Bridge Treiber nicht laden"); return; } Connection con = DriverManager.getConnection( "jdbc:odbc:myDatabase", "myLogin", "myPassword"); Treiber lassen sich mit Class.forName laden Oben wird im JDK enthaltener JDBC-OBDC Bridge Treiber geladen ODBC Datenquellen werden mit jdbc:odbc:Name-der-Datenquelle referenziert Diese Folie verdeutlicht die Nutzung des im JDK enthaltenen JDBC-ODBC Bridge Treibers zum Zugriff auf eine ODBC Datenquelle (z.B. ein Access Datenbank). Hierzu muss zunächst die Treiberklasse geladen werden. Dies geschieht mit der forName() Methode der Klasse Class. Diese lädt die Klasse, deren Klassenname als Argument übergeben wird, in die virtuelle Maschine. Jeder JDBC Treiber hat innerhalb seiner Klassendefinition einen statischen Initialisierungsteil, der von der virtuellen Maschine nach Laden der Klasse ausgeführt wird (solche statischen Initialisierungsteile werden bei Laden der Klasse nicht bei der Objekterzeugung einmalig ausgeführt - im Unterschied zu den Konstruktor-Methoden, die bei der Objekterzeugung ausgeführt werden). Dieser statische Initialisierungscode erzeugt ein neues Treiberobjekt und registriert dieses über Aufruf der statischen Methode registerDriver() der DriverManager Klasse der JDBC API im DriverManager. Nachdem der Treiber (der im JDK enthaltene JDBC-ODBC Treiber hat den Klassennamen sun.jdbc.odbc.Jdbc.OdbcDriver) auf diese Weise geladen wurde, kann eine Verbindung zu der gewünschten Datenbank aufgebaut werden. Die Datenbank muss zuvor als ODBC Datenquelle unter einem bestimmten Namen (in unserem Falle myDatabase) registriert werden. Die Datenbank kann dann beim Verbindungsaufbau durch den String jdbc:odbc:myDatabase als JDBC Bezeichner der Datenbank angesprochen werden. Die Verbindung fordert man dann mit diesem Namen unter Verwendung der getConnection() Methode der DriverManager Klasse an, wobei man - falls für die ODBC Datenquelle Benutzer und Passwort festgelegt wurden - gegebenenfalls noch Benutzername und Passwort zum Zugriff auf die ODBC Datenquelle angeben muss. Der DriverManager scannt bei Aufruf der getConnection() Methode die interne Liste der registrierten Treiber und sucht nach einem geeigneten odbc Treiber (siehe 2. Komponente im jdbc:odbc:myDatabase String). Über diesen Treiber wird dann der eigentliche Verbindungsaufbau durchgeführt. Als Ergebnis erhalte ich ein Connection Objekt, über das dann die weiteren Operationen mit der Datenbank durchgeführt werden können. Clemens Düpmeier,

35 JDBC-MSQL Datenquelle
try { Class.forName("com.imaginary.sql.msql.MsqlDriver"); } catch (Exception e) { System.out.println("Konnte JDBC Treiber nicht laden"); return; } Connection con = DriverManager.getConnection( "jdbc:msql://machine-name:port#/database-name", "myLogin", "myPassword"); Treiber lassen sich mit Class.forName laden Oben Treiber für mSQL Datenbank geladen Datenquellen werden allgemeiner durch URL's der Form jdbc:subprotocol:subname referenziert Auf dieser Folie wird nicht auf eine ODBC Datenquelle sondern eine relatioale Datenbank (MSQL) zugegriffen. Der zugehörige Treiber ist hier com.imaginary.sql.msql.MsqlDriver. Jeder spezielle Treiber hat neben einer eindeutigen Treiberkennung (in diesem Fall msql= zweiter Teil der JDBC Datenquellenbezeichnung) ein eigenes Schema, wie die spezielle Datenbank, die anzusprechen ist, im JDBC Datenquellennamen zu bezeichnen ist. Treiber von relationalen Datenbanken, die auch Remote angesprochen werden können, schreiben als dritte Komponente des Namens in der Regel einen String von der Gestalt //hostname/port#/database-name (also die Angabe von Hostnamen, auf dem der Datenbankserver läuft, die Angabe der Portnummer, unter der die Datenbank zu erreichen ist, sowie die Angabe des Datenbanknamens vor). Die exakte Beschreibung, wie dieser dritte Teil der Datenquellenbeschreibung aussehen sollte, muss man allerdings der Beschreibung des jeweiligen Treibers entnehmen. Ein Verbindungsobjekt erhält man wiederum über die getConnection() Methode der DriverManager Klasse. Clemens Düpmeier,

36 DriverManager Klasse DriverManager
static Connection getConnection(String url) static Connection getConnection(String url, Properties info) static Connection getConnection(String url, String user, String password) // verschiedene Arten Verbindung aufzubauen ... static void registerDriver(Driver driver) // wird von Treibern zum Registrieren verwendet static void setLogWriter(PrintWriter out) // Loggen von Informationen über JDBC Protokollverkehr stativ void println(String message) // Ausgabe von Messagestrings auf Logging Ausgang DriverManager Klasse: bietet Treiber registerDriver Methode, um sich zu registrieren. Erlaubt es dem Programmierer Ausgabe PrintWriter zum Loggen des Protokollverkehrs zu setzen. Mit der println() Methode kann man eigene MessageStrings in diese Ausgabe mit einfügen. Die DriverManager Klasse bietet hauptsächlich die getConnection() Methoden, um eine Verbindung mit einer Datenbank über einen registrierten Treiber aufzubauen. Clemens Düpmeier,

37 Verschiedene Statement-Objekte
Statement Objekt erzeugen Statement stmnt = con.createStatement() PreparedStatement Objekt erzeugen PreparedStatement pstmnt = con.prepareStatement("...") CallableStatement Objekt erzeugen CallableStatement cstmnt = con.prepareCall("...") Aktionen auf der Datenbank lassen sich unter Verwendung von 3 Objektklassen durchführen, deren Objekte als Container zur Abwicklung der gewünschten Datenbankaktion dienen. Dies sind Statement, PreparedStatement und CallableStatement Objekte. Alle Statement Objekte werden über das Verbindungsobjekt zur Datenbank, das Connection Objekt, erzeugt. Clemens Düpmeier,

38 Verwendung der verschiedenen Statement Objektklassen
Statement für einfache Operationen PreparedStatement Objekte verwenden, wenn SQL Abfragen öfters wiederholt werden CallableStatement Objekte nutzen, um StoredProcedures auszuführen PreparedStatement Objekte erzeugen Schablonen für häufig wiederkehrende SQL Anfragen, in denen nur die Werte bestimmter Parameter der Anfrage substituiert werden müssen. Dies ist natürlich performanter, als die Verwendung einfache Statement Objekte, bei denen die gesamte SQL Anfrage als Textstring bei Durchführung der eigentlichen Abfrage angegeben wird und dann erst auf das Zielformat gebracht werde muss. Über ein CallableStatement Objekt kann selbstdefinierte, innerhalb der Datenbank gespeicherte Prozeduren (Stored Procedures) aufrufen. Stored Procedures werden häufig zur Optimierung komplexer, zeitintensiver Operationen in Datenbanken verwendet. Bei Statement oder PreparedStatement Objekten ist die Basis für jede Operation auf der Datenbank SQL Code. Während dieser bei Statement direkt innerhalb der Methode des Statement Objektes, die die tatsächliche Aktion auf der Datenbank ausführt, angegeben wird (executeQuery() oder executeUpdate() Methode des Statement Objektes), wird der SQL bei einem PreparedStatement schon bei dessen Erzeugung als Argument des Konstruktors angegeben, wobei Platzhalter im SQL Statement die Stellen markieren, die später bei Durchführung der Aktion durch Werte übergebender Parameter ersetzt werden. Im folgenden wollen wir uns die Verwendung von Statement Objekte ansehen. Clemens Düpmeier,

39 Datenbankabfrage über Statement Objekt durchführen
try { // Statement Objekt über Connection Objekt erzeugen Statement stmt = con.createStatement(); // SQL Abfrage als String definieren String query = "SELECT cityName, Population, Temperature" " FROM cityTable"; // Abfrage durch Aufruf von Methode executeQuery() durchführen ResultSet rs = stmt.executeQuery(query); // hier nun Ergebnisse durch Auswertung von ResultSet // verarbeiten Datenbankabfragen werden über ein Statement Objekt mit Aufrufe der Methode executeQuery() des Statement Objektes durchgeführt. Dabei wird der SQL Code für die Abfrage als String beim Aufruf angegeben und nicht etwa bereits bei der Erzeugung des Statement Objektes. Clemens Düpmeier,

40 ResultSet abarbeiten try { ...
// Abfrage durch Aufruf von Methode executeQuery() durchführen ResultSet rs = stmt.executeQuery(query); while (rs.next()) { System.out.println("Stadt: " + rs.getString("cityName")); System.out.println("Anzahl Einwohner: " + rs.getLong("Population")); System.out.println("Durchschnittstemperatur: " rs.getInt("Temperature")); } } catch ... Das ResultSet Objekt, das man als Returnwert des Aufrufs der Methode executeQuery() der Klasse Statement erhält, enthält die gewünschte Resultattabelle. Mit der next() Methode kann man den Cursor auf die aktuelle Tabellenzeile der Resultattabelle jeweils um eine Position weiter bewegen. Am Anfang steht der Cursor vor der ersten Zeile. Befindet sich der Cursor auf der letzten Zeile wird null zurückgegeben. Auf die einzelnen Spaltenelemente der aktuellen Zeile kann man durch Methoden, wie getString(), getLong() oder getInt() zugreifen, wobei man als Argument entweder den Namen der Tabellenspalte oder den Index der gewünschten Tabellenspalte angibt. Welche Methode man wählt, hängt davon ab, von welchem Typ der Wert in der gewählten Spalte ist bzw. sein sollte. Die nächste Folie zeigt möglichen Zugriffsmethoden und ihre Zuordnung zu SQL Typen. Clemens Düpmeier,

41 Zugriffsmethoden und SQL Datentypen
getInt() INTEGER getLong() BIG INT getFloat() REAL getDouble() FLOAT getBignum() DECIMAL getBigDecimal() NUMBER getBoolean() BIT getString() VARCHAR getString() CHAR getAsciiStream() LANGVARCHAR getDate() DATE getTime() TIME getTimestamp() TIME STAMP getObject() jeder Typ Die obige Folie zeigt, welche Zugriffsmethode für welchen Spaltentyp verwendet werden kann. Neben diesen Methoden gibt es noch weitere. Für eine vollständige Übersicht sehe man sich die JDBC Dokumentation an. Clemens Düpmeier,

42 Daten in Datenbank einfügen
try { // Statement Objekt über Connection Objekt erzeugen Statement stmt = con.createStatement(); // SQL Statement zum Einfügen von Daten definieren String sqlstring = "INSERT INTO cityTable " + "(cityName, Population, Temperature) " + "VALUES (Karlsruhe, , 15)"; // Einfügen mit Aufruf der Methode executeUpdate() durchführen int affectedRows = stmt.executeUpdate(sqlstring); Zum Einfügen neuer Daten in die Datenbank, zum Löschen von Zeilen aus Tabelle und zum Update von Daten verwendet man die Methode executeUpdate() des Statement Objektes, wobei man die SQL Anweisungen INSERT, DELETE oder UPDATE als SQL String als Parameter übergibt. Clemens Düpmeier,

43 Client-Server Architekturformen
Verteilte Präsentation Entfernte Präsentation Verteilte Anw.logik Entfernte Datenbank Verteilte Datenbank Client Präsentationslogik Präsentationslogik Präsentationslogik Präsentationslogik Präsentationslogik Anwendungslogik Anwendungslogik Anwendungslogik Datenbanklogik Präsentationslogik Anwendungslogik Anwendungslogik Anwendungslogik Datenbanklogik Datenbanklogik Datenbanklogik Datenbanklogik Datenbanklogik Server Clemens Düpmeier,

44 2-Tier Applikation mit JDBC Datenzugriff
Java Applikation Client Maschine JDBC proprietäres Protokoll zu Datenbank DBMS Von Java aus kann man mit der JDBC API auf Datenbanken zugreifen. Je nach JDBC Treiber kann dies direkt über das Netzwerk von einem entfernten Rechner aus passieren. Hierzu nutzt der JDBC Treiber ein proprietäres Anwendungsprotokoll zur Kommunikation mit dem Datenbankserver. Auf diese Weise lassen sich 2-Tier Applikationen erstellen, die direkt auf eine Datenbank über Netz zugreifen. Rechner mit Datenbank Clemens Düpmeier,

45 Multitier Architekturen
Bei Multitier Architekturen ist die Software eines Gesamtsystems in mehrere Schichten zerlegt Typisch ist eine 3-Tier Architektur mit den Schichten Datenbanklogik, Anwendungslogik und Präsentationslogik Die einzelnen Schichten sind durch Interfaces voneinander getrennt und gegebenenfalls durch verteilte Objekttechnologie auf verschiedene Rechner verteilt Clemens Düpmeier,

46 Klassische Multitier Architektur
Präsentationsschicht Applikationslogik Datenlogik Datenlogik Clemens Düpmeier,

47 Klassische 3-Tier Architektur mit JDBC
Client z.B. Browser Client Maschine HTTP, RMI, CORBA Applikationsserver Server mit Middle-Tier (Applikationslogik) JDBC proprietäres Protokoll zu Datenbank DBMS Eine klassische 3-Tier Architektur verwendet zwischen Benutzeroberfläche und Datenbank einen Server (Applikationsserver) in der Mitte, der die Anwendungslogik beinhaltet und z.B. über JDBC auf die entfernte Datenbank zugreift. Rechner mit Datenbank Clemens Düpmeier,

48 4-Tier Architektur mit JDBC
Client z.B. Browser Client Maschine HTTP Webserver Servlet RMI, CORBA Applikationsserver Server mit (Applikationslogik) JDBC Die eigentliche Applikationslogik laesst sich weiter von der Oberfläche und der Steuerungslogik (dem Controller-Teil) einer Applikation trennen, wenn man wie im obigen Beispiel ein 4-Tier Architektur verwendet, bei der z.B. der Webserver-Servlet Teil sich auf einem anderen Rechner wie der Applikatonsserver befindet. Das Servlet kommuniziert hier über RMI oder CORBA mit dem Applikationsserver. Dieser benutzt JDBC, um auf die entfernte Datenbank zuzugreifen. Wesentliche Aufgabe des Applikationsservers in diesem Scenario ist die Transformation der tabellarischen Daten in eine objektorientierte API, die dann über RMI dem Servlet angeboten wird. Diese Applikationsserver kapselt dann weitere Funktionalitäten, wie Transaktionssemantik, Aktivierbarkeit, und Parallelisierung (Threading) der Anwendungsobjekte. proprietäres Protokoll zu Datenbank DBMS Rechner mit Datenbank Clemens Düpmeier,

49 Middleware Mittelschicht zwischen Teilen einer Verteilten Applikation
verdeckt Low-Level Schnittstellen – insbesondere untere Kommunikationsschicht vereinfacht das Schreiben verteilter Systeme mit Mehrschicht-Architektur erlaubt es Anwendungen, sich auf die Applikationsdetails zu konzentrieren Clemens Düpmeier,


Herunterladen ppt "Client-Server Kommunikation"

Ähnliche Präsentationen


Google-Anzeigen