Netzwerk Programmierung Ein großer Teil von dem, was Netzwerkprogramme tun ist ganz simpler input und output: also bytes verschieben von einem System zu einem anderen. Bytes bleiben Bytes. Die Daten zu lesen, die ein Server sendet ist nicht viel anders als von einem file zu lesen. Text zu einem Client senden, ist auch nicht viel anders als ein file zu schreiben. Trotzdem ist das I/O in JAVA anders organisiert als in anderen Sprachen, wie z.b. Fortran, C, C++. I/O in JAVA basiert auf Streams. Es gibt verschiedene stream Klassen Java.io.FileInputStream Sun.net.TelnetOutputStream Einlesen und Auslesen basiert auf allen Stufen fast gleich. Hat man erst einmal einen Stream erzeugt braucht man sich um die Details gar nicht mehr kümmern. Filter Streams können hintereinander verkettet werden. Filter können die Daten verändern, z.b. verschlüsseln, komprimieren. Normalerweise brauchen wir nur das Basis Modell der Stream für Clients. Output Streams Klassen http://www.falkhausen.de/de/diagram/html/java.io.outputstream.html 1
Wobei dies die fundamentalen Methoden sind um Daten zu schreiben. Die Unterklassen von Output Stream benutzen diese Methoden um Daten auf verschiedene Medien zu schreiben. z.b. File OutputStream Daten in ein File TelnetOutputStream Daten auf eine Netzwerk Verbindung ByteArrayOutputStream Daten in Byte Array Egal wo wir hinschreiben, wir benutzen meistens nur diese 5 Methoden. Den TelnetOutputStream finden wir nicht in der Klassendokumentation von JAVA, er ist versteckt in den Sun Packages. Daten können verloren gehen, wenn wir unseren Stream nicht flushen. (to flush = durchspülen) 2
Streams können in Software gepuffert werden, im Java Sourcecode direkt ebenso wie in der Netzwerk Hardware. Daraus folgt konsequenterweise, dass wenn wir geschrieben haben, wir den output Stream flushen müssen. Wir sahen dies schon an einem Beispiel. Beispiel: Angenommen wir hätten eine 300 byte anfrage (request) an einen http 1.1 Server geschrieben, welcher http Keep Alive verwendet. Kennen wir schon aus CISCO. Trotzdem hier eine Wiederholung. http://de.wikipedia.org/wiki/hypertext_transfer_protocol Keepalive ist ein Mechanismus bei der Datenübertragung mit zwei Zielen: eine Netzwerkverbindung am Leben zu halten, sich selbst von Erreichbarkeit und Funktion eines Kommunikationspartners zu überzeugen. Keepalives sind in der Regel spezifische Pakete eines Netzwerkprotokolls. Sie werden in regelmäßigen Abständen durch einen bestehenden Kommunikationskanal zwischen den Partnern ausgetauscht. Vom Empfänger einer solchen Nachricht wird innerhalb einer Zeitschranke eine Reaktion erwartet. Bleibt das Keepalive Paket oder die Reaktion darauf (ggf. mehrfach) aus, geht der entsprechende Kommunikationspartner von einer Unterbrechung der Verbindung oder einer Nichtfunktion des Kommunikationspartners aus und ergreift weitere Maßnahmen. Solche Maßnahmen können sein: Wiederaufsetzen der Netzwerkverbindung bei bestehendem Protokoll (z. B. Fortsetzen eines Dateidownloads, Sitzungsmanagement bei HTTP und VPN Verbindungen), erneute Verbindungaufnahme unter Neustart des Protokolls (z. B. Mailversand über SMTP), endgültige Verbindungsaufgabe (Abbruch), d. h. Beendigung des Protokolls mit einer Fehlermeldung. Üblicherweise warten wir auf eine Antwort, bevor wir wieder mehr Daten senden. Angenommen, der Output Stream hat einen buffer von 1024 byte, dann wartet der Stream auf Daten bis er gefüllt ist, bevor er sendet. Es werden keine Daten auf den Stream geschrieben, bevor der Server nicht antwortet. Wie soll aber der Server antworten, wenn die Daten gar nicht gesendet wurden. Usw. Deadlock s. Abb. Auf Seite 2. Die flush() Methode unterbricht den Deadlock, da sie die Daten sendet obwohl der Puffer noch nicht voll ist. Am besten ist es immer die Streams zu flushen. System.out wird z.b. gepuffert, ob wir das wollen oder nicht. Also vor dem Schließen der Streams immer flushen. Sonst kann es sein, dass Daten verloren gehen. 3
Filter Streams InputStream und OutputStream sind sehr einfache Klassen. Sie lesen und schreiben Bytes in Gruppen, das ist alles. Die Entscheidung, was diese Bytes bedeuten Ganze Zahlen IEEE 754 Kommazahlen Unicode Text Etc. Liegt voll auf der Seite des Programmierers und dessen Code. Es gibt einige übliche Formate für die Übertragung. 32 bit big endian integers http://de.wikipedia.org/wiki/little_endian Text in 7bit ASCII, 8 bit Latin 1, multi byte utf 8 Files per ftp im zip format Etc. JAVA stellt einige Filter Klassen zur Verfügung, die man auf die Rohdaten anwenden kann um die Bytes zu übersetzen. Die Filter gibt es in zwei Versionen: Filter streams Readers and writers 4
Filter sind in einer Kette organisiert, wie in folgender Abb. zeigt. Zum Verschlüsseln von Datenströmen bietet das Java SDK die praktischen Klassen javax.crypto.cipherinputstream und CipherOutputStream an. Sie erwarten ein Cipher Objekt, das eine DES Verschlüsselung durchführt. http://de.wikipedia.org/wiki/cipher_block_chaining_mode (Cipher Chiffre, Geheimschrift) 5