Client-Server Kommunikation

Slides:



Advertisements
Ähnliche Präsentationen
Object Relational Mapping
Advertisements

1 Kapitel 9: Datenbankapplikationen. 2 Datenbankapplikationen MS Access Embedded SQL JDBC Application JDBC Applet Java Servlet Java Server Pages Cold.
DVG Dateien Dateien. DVG Dateien 2 Die Klasse File Die Klasse File stellt die Verbindung zwischen dem Filesystem des Rechners und dem.
PKJ 2005/1 Stefan Dissmann Vorwoche - Klasse public class Studierende { private String name, vorname, studiengang; private int matNr, semester; private.
Basis-Architekturen für Web-Anwendungen
Internet-Datenbanken
Prof. Dr.-Ing. habil. B. Steinbach - Informatik / Softwaretechnologie und Programmierungstechnik - Institut für Informatik Verteilte Software - Java -
Tomcat Web-Server installieren
Internetzugriff mit Strings und Streams
10 Streams JavaHS Merseburg WS 05/06 E/A - Ströme (Streams) in Java.
Internet-Datenbanken Grundlagen des WWW HTML HTTP Web-Anbindung von Datenbanken Servlets JSP JDBC XML Datenmodell Schemabeschreibungssprachen Anfragesprachen.
Java: Objektorientierte Programmierung
Java: Dynamische Datentypen
Indirekte Adressierung
FH-Hof Sockets in Java Richard Göbel. FH-Hof Kommunikation über das Internet - Grundlagen Ein Rechner wird im Internet über die so genannte IP- Adresse.
FH-Hof SQLJ Richard Göbel. FH-Hof SQLJ - Idee Erweiterung von Java um SQL Die Verwendung von SQL-Anweisungen innerhalb einer Programmiersprache wird vereinfacht.
FH-Hof Verwaltung von Zeichenketten Richard Göbel.
Java: Grundlagen der Sprache
Java: Referenzen und Zeichenketten
FH-Hof Fehlerbehandlung Richard Göbel. FH-Hof Konzept Fehler können mit dem Operator throw einer übergeordneten Funktion signalisiert werden. Parameter.
Benötigte Applets Startseite: in HTML-Format Applet auf der Startseite Das Applet, das auf der Startseite geladen wird, wird die vier Buttons und die eine.
Dynamische Webseiten mit PHP
Dynamische Webseiten Java servlets.
Datenbankanwendungen mit Java und JDBC
Objektorientierte Programmierung JDK-Klassenbibliothek
JDBC Konzepte Realisierung von Zugriffen
Technik Gestaltung Navigation Daten. Übersicht Client Webbrowser InternetServer.
Institut für Kartographie und Geoinformation Prof. Dr. Lutz Plümer Diskrete Mathematik I Vorlesung Listen-
Vererbung Spezialisierung von Klassen in JAVA möglich durch
PKJ 2005/1 Stefan Dissmann Rückblick auf 2005 Was zuletzt in 2005 vorgestellt wurde: Klassen mit Attributen, Methoden und Konstruktoren Referenzen auf.
PKJ 2005/1 Stefan Dissmann Zusammenfassung Bisher im Kurs erarbeitete Konzepte(1): Umgang mit einfachen Datentypen Umgang mit Feldern Umgang mit Referenzen.
Transaction Script Software Component Technology for Distributed Applications.
JAVA RMI.
JDBC -Java Database Connectivity-. 15./22. April 2004JDBC2 JDBC.... verbindet Java-Programme mit SQL-basierten Datenbanken.. liefert eine generische SQL-API.
DVG Kommentare1 Kommentare. DVG Kommentare 2 Kommentare Es gibt zwei Arten von Kommentaren: einzeilige Kommentare // der Kommentar geht.
EDV Parallelprogrammierung1 Parallelprogrammierung mit JAVA.
DVG Kommentare 1 Kommentare. 2 Kommentare Es gibt zwei Arten von Kommentaren: einzeilige Kommentare // der Kommentar geht bis zum Ende der Zeile.
JDBC EDV JDBC.
© 2005 Pohlig - Taulien Datenströme GK Informatik 1 Datenströme.
Einführung MySQL mit PHP
Prof. K. Gremminger Folie 1 Vorlesung Datenbanksysteme SS 2002 Aufbau einer Verbindung zur Datenbank import java.net.URL; import java.sql.*; class JDBCExample.
JDBC: JAVA Database Connectivity
Datenmodelle, Datenbanksprachen und Datenbankmanagementsysteme
ODBC (Open Database Connectivity)
Einführung Servlets/JSPs
20:00.
Learning By Doing TCP/IP Netzwerke mit TCP/IP Das Internet verwendet weitgehend das rund 30-jährige TCP/IP-Protokoll (TCP: Transmission Control Protocol,
Socket-Programmierung
Allgemeine Technologien I Sitzung am Mailserver
Netzwerkprogrammierung
IFB Speyer Daniel Jonietz dj 2 XAMPP - Was ist das? Paket mit: – X – Apache (Webserver) – MySQL oder SQLite (Datenbank) – Perl (Skriptsprache) –
HORIZONT 1 XINFO ® Das IT - Informationssystem PL/1 Scanner HORIZONT Software für Rechenzentren Garmischer Str. 8 D München Tel ++49(0)89 / 540.
JDBC (Java DataBase Connectivity)
CuP - Java Eingabe über Tastatur, AudioClips, überschreiben, Quiz Montag, 18. November 2002.
CuP - Java Neunte Vorlesung Entspricht Kapitel 4.2 und 5 des Skriptums
CuP - Java Vierte Vorlesung Entspricht ungefähr Kapitel 2.1 des Skriptums Montag, 14. Oktober 2002.
Datenbankanbindung mit
HTTP IT-Zertifikat Universität zu Köln Allgemeine Technologien II
Voyager Eigenschaften/Vorzüge Universalität: –ROI-Modelle: CORBA, RMI, DCOM –verschiedene Namens-, Verzeichnisdienste Nachrichtentypen: synchron, oneway,
CuP - Java Achte Vorlesung Entspricht ungefähr Kapitel 4.1 des Skriptums Montag, 28. Oktober 2002.
prof. dr. dieter steinmannfachhochschule trier © prof. dr. dieter steinmann Folie 1 vom Montag, 30. März 2015.
Alois Schütte Advanced System Programming 2 Interprozeßkommunikation  2.1 JVM Ablaufumgebung  2.2 Java Native Interface (JNI)  Verwendung von.
Client-Server Kommunikation
->Prinzip ->Systeme ->Peer – to – Peer
1 Medienpädagogischer Forschungsverbund Südwest KIM-Studie 2014 Landesanstalt für Kommunikation Baden-Württemberg (LFK) Landeszentrale für Medien und Kommunikation.
1 Servlets Stephan Baldes. 2 Was ist ein Servlet? S E R V L E T = Eine auf Java-Technologie basierte Web-Komponente, die von einem Container.
Netzwerk - Programmierung
Netzwerk - Programmierung
JDBC Java DataBase Connectivity
Tutorstunde 10.
 Präsentation transkript:

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

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, 28.03.2017

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, 28.03.2017

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, 28.03.2017

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, 28.03.2017

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, 28.03.2017

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 2002 22:52:47 +0200 HELO uisun7.iai.fzk.de 250 mailhost.iai.fzk.de Hello uisun7.iai.fzk.de MAIL From: <duepmeier@iai.fzk.de> 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 Email 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 Email im Email 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, 28.03.2017

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/1.0 200 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/1.0 200 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, 28.03.2017

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, 28.03.2017

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: http://tecfa.unige.ch:7778/4848 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, 28.03.2017

HTTP Server Statuscodes 1xx: Informierend, 2xx: Erfolgreich 3xx: Rückfrage, 4xx: Fehler beim Client 5xx: Fehler beim Server 200 OK 201 Created 202 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, 28.03.2017

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, 28.03.2017

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 (141.53.44.26), 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 141.52.44.1 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, 28.03.2017

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, 28.03.2017

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, 28.03.2017

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, 28.03.2017

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, 28.03.2017

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, 28.03.2017

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, 28.03.2017

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, 28.03.2017

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, 28.03.2017

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, 28.03.2017

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, 28.03.2017

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, 28.03.2017

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, 28.03.2017

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, 28.03.2017

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.03.2017

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

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, 28.03.2017

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

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, 28.03.2017

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, 28.03.2017

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, 28.03.2017

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, 28.03.2017

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, 28.03.2017

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, 28.03.2017

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, 28.03.2017

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, 28.03.2017

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, 28.03.2017

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, 28.03.2017

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, 28.03.2017

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, 275000, 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, 28.03.2017

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, 28.03.2017

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, 28.03.2017

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, 28.03.2017

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

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, 28.03.2017

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, 28.03.2017

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, 28.03.2017