Netzwerkprogrammierung mit Java

Ähnliche Dokumente
Verteilte Systeme - Java Networking (Sockets) -

Referat: Netzwerkprogrammierung in Java

Client-Server TCP/IP - Kodierung

Java und Netzwerkkommunikation

Java I/O, Serialisierung und Netzwerkprogrammierung

Network Communication. Dr. Jürgen Eckerle WS 06/07

Programmieren II. Timer. Vorlesung 11. Handout S. 1. Martin Schultheiß. Hochschule Darmstadt Sommersemester Timer. Sockets.

Jan Distel. Im Rahmen der Veranstaltung Fortgeschrittene Programmierung in Java

Client-Server TCP/IP - Kodierung

TCP und UDP Sockets in Java

Softwarepraktikum Sommersemester 2006

Programmiermethodik. Übung 10

Transmission Control Protocol (TCP)

Netzwerkprogrammierung

Probeklausur: Programmierung WS04/05

Programmieren II. Sockets. Vorlesung 16. Handout S. 1. Dr. Klaus Höppner. Hochschule Darmstadt Sommersemester Sockets.

Socket-Programmierung unter Java

Tag 8 Repetitorium Informatik (Java)

Advanced Network Programming

Serielle Kommunikation - Kodierung

Dr. Monika Meiler. Inhalt

Netzwerkprogrammierung & Threads

Verbindungen zu mehreren Clients. Informatik B - Objektorientierte Programmierung in Java. Vorlesung 23: Netzwerkprogrammierung/ Kommunikation 2

Rechnernetze und verteilte Systeme Programmieraufgabe

Einführung Verteilte Systeme - Java Threads I -

NIO Channels SYSTEM SOFTWARE 1

Client-Server TCP/IP - Kodierung

Daniel Warneke Ein Vortrag im Rahmen des Proseminars Software Pioneers

Neben der Verwendung von Klassen ist Vererbung ein wichtiges Merkmal objektorientierter

1. Grundzüge der Objektorientierung 2. Methoden, Unterprogramme und Parameter 3. Datenabstraktion 4. Konstruktoren 5. Vordefinierte Klassen

Java RMI Remote Method Invocation

Netzwerkprogrammierung unter Linux und UNIX

ebusiness Übung 3a Spezifikation und Nutzung von Web-APIs (Services) Prof. Dr. Andreas Schmietendorf 1

Programmiermethodik 1. Klausur

1 Abstrakte Klassen, finale Klassen und Interfaces

Interface. So werden Interfaces gemacht

Erzeugungsmuster. Kapselung der Objekt-Erzeugung

4. Thread- und Netzwerk- Programmierung

Beispiel Time Client/Server

Kommentare, Client-Server, Protokolle

Projektarbeit Java. 4-Gewinnt. Berner Fachhochschule. 2004, Labor für Technische Informatik

Programmiermethodik. Übung 13

EINFÜHRUNG IN DIE PROGRAMMIERUNG

Vorkurs Informatik WiSe 17/18

II.3.1 Rekursive Algorithmen - 1 -

Programmieren in Java

Java Einführung Exception Handling. Kapitel 17

Einführung in die Informatik: Programmierung und Software-Entwicklung, WS 11/12 1. Kapitel 11. Listen. Listen

Programmiermethodik 3. Klausur Lösung

Vorkurs Informatik WiSe 16/17

12 Abstrakte Klassen, finale Klassen und Interfaces

TECHNISCHE UNIVERSITÄT MÜNCHEN FAKULTÄT FÜR INFORMATIK

Handbuch für die Erweiterbarkeit

Java: Kapitel 1. Überblick. Programmentwicklung WS 2008/2009. Holger Röder Holger Röder

Einführung in Java. Ausgewählte Quellen zu Java

Creational Patterns. Seminar Software-Entwurf. Thomas Liro WS 2004/05.

Repetitorium Informatik (Java)

II. Grundlagen der Programmierung. Beispiel: Merge Sort. Beispiel: Merge Sort (Forts. ) Beispiel: Merge Sort (Forts. )

Zentralübung Grundlagen der Programmierung

Objektorientierte Programmierung und Modellierung

Grundlagen der Programmierung Prof. H. Mössenböck. 15. Pakete

Programmierkurs Java

Einstieg in die Informatik mit Java

Objektorientierte Programmierung. Kapitel 22: Aufzählungstypen (Enumeration Types)

Netzwerkprogrammierung & Threads

Beispiel: Temperaturumwandlung. Imperative Programmierung. Schwerpunkte. 3. Grundlegende Sprachkonstruktionen imperativer Programme

Ausnahmebehandlung in Java

14 Abstrakte Klassen, finale Klassen, Interfaces

6 Speicherorganisation

Heap vs. Stack vs. statisch. 6 Speicherorganisation. Beispiel Statische Variablen. Statische Variablen

TCP/IP Programmierung. C# TimeServer Java6 TimeClient

Problem: Was ist, wenn der Stapel voll ist? Idee: Erzeuge dynamisch ein grösseres Array und kopiere um. Dynamische Anpassung der Größe

14 Abstrakte Klassen, finale Klassen, Interfaces. Auswertung von Ausdrücken. Beispiel. Abstrakte Methoden und Klassen

Remote Method Invocation

JUnit. Software-Tests

Klausur Grundlagen der Programmierung

Heap vs. Stack vs. statisch. 6 Speicherorganisation. Beispiel Statische Variablen. Statische Variablen

Einstieg in die Informatik mit Java

Systemprogrammierung. Projekt: Java RMI. Wintersemester 2006 / 2007

Versuchsziele Konzepte der parallelen Programmierung am Beispiel von Threads anwenden können. Einbau von Kontrollmechanismen mittels Semaphore.

Info B VL 8: Abstrakte Klassen & Interfaces

Unified-E Standard WebHttp Adapter

Objektorientierung. Klassen und Objekte. Dr. Beatrice Amrhein

Grundkurs Programmieren in Java

Transkript:

Netzwerkprogrammierung mit Java Eine Übersicht über Java NIO Andre Ufer a09008@hb.dhbw-stuttgart.de Zusammenfassung. Diese Ausarbeitung beschreibt die Netzwerkprogrammierung unter Java. Der Schwerpunkt liegt dabei auf einem Vergleich zwischen den Bibliotheken bzw. Frameworks java.net und java.nio. bezüglich ihrer Fähigkeiten, Vor- und Nachteile. Betrachtet werden dabei Umsetzungen einfacher Server und Einsatzzwecke und implementierende Frameworks. 1 Einführung 1.1 Motivation Das Internet ist heute aus der modernen Kommunikation nicht mehr wegzudenken und dabei größter und bekanntester Vertreter und Grundlage sogenannter Verteilter Systeme. Dabei meint die Kommunikation nicht nur den Austausch von Informationen zwischen verschiedenen Personen, sondern auch der ständige Datenaustausch nahezu beliebiger (Software-)Komponenten. Durch den hohen Grad der Vernetzung von Hard- und Software benötigt man klare Strukturen, einheitliche Schnittstellen und Konzepte, um diese Kommunikation zu ermöglichen. Sie lässt sich dabei aus technischer Sicht aus mehreren Blickwinkeln betrachten. Auf der einen Seite gibt es die technische bzw. hardwarebasierte Sicht. Hierunter fallen alle Komponenten, die die technische Basis dieser Kommunikation bilden. Im ISO-OSI-Schichtenmodell würden wir von der Physical Layer oder Schicht 1 reden. Auf der anderen Seite steht die Software. Unter Software ist in diesem Zusammenhang nicht nur ein ausführbares Stück Programmcode zu verstehen, sondern alle Schichten des ISO/OSI-Modells von zwei bis sieben. Darunter fallen Adressierungskonzepte (z.b. MAC), Übertragungsprotokolle (z.b. TCP/IP, HTTP) sowie beliebig abstrakte Implementierungen, die eine Kommunikation zwischen vernetzen Komponenten bzw. Verteilten Systemen ermöglichen. Java NIO gehört zur letzteren Kategorie und soll im Rahmen dieser Arbeit mit der alten Java Netzwerk-API 1 2 mit Bezug auf Nutzen, Vor- und Nachteile verglichen werden. 1 Java NIO befindet sich im Paket java.nio, die alte API in java.net 2 Im weiteren Verlauf wird die java.net API inkl. der benötigten Klasen aus java.io als NET, die java.nio API als NIO bezeichnet

1.2 Was ist Java NIO Die Java new I/O (NIO) API wurde mit dem JDK v1.4 veröffentlicht und erweitert die Java API vor allem um Funktionen zur Dateibehandlung und zur Netzwerkkommunikation. Sie ist Buffer-orientiert und nicht-blockierend. Die alten APIs java.io und java.net hingegen sind Stream-orientert und blockierend. Der Schwerpunkt dieser Arbeit liegt auf den neuen Möglichkeiten in der Netzwerkprogrammierung, die um Funktionen wie Channels für Buffer, Selectors für non-blocking I/O und Erweiterungen zur klassischen Socket-Programmierung ergänzt wurden. Ziele von NIO sind vor allem eine bessere Performance und Skalierbarkeit durch die nicht-blockierenden Implementierung der neuen Funktionen. 2 Grundlagen der Netzwerkprogrammierung 2.1 Java NET und Java NIO Die Netzwerkprogrammierung mittels NET erfolgt über klassische Programmierung mittels Sockets. Dabei fordert der Client eine Verbindung an, indem eine Instanz der java.net.socket-klasse erstellt wird. Als Parameter werden i.d.r. Server-Url und Portnummer verwendet. Der Server arbeitet mit einer Instanz der Klasse java.net.serversocket. Dieser bekommt als Parameter den Port, auf den er lauschen soll. Der Verbindungsaufbau findet über die accept()-methode des ServerSockets statt. Nach Verbindungsaufbau findet anschließend ein Nachrichtenaustausch statt. Dieser erfolgt in der java.net API streamorientiert. Jeder Socket bzw. ServerSocket besitzt dazu Input- sowie einen OutputStream, mit dem Daten via Byte-Arrays übertragen werden können. NIO arbeitet (zumindest an der Oberfläche) grundsätzlich anders. Die Datenübertragung erfolgt nicht mehr stream- sondern blockorientiert über sogenannte Channels. Die übertragenen Daten werden mittels Buffer gekapselt. Ein einfaches NIO-Beispiel folgt im nächsten Kapitel. 2.2 Beipsiel: einfacher Server Es folgt ein einfacher Server, der ausschließlich mit den Klassen aus der java.net API programmiert wurde. Das Lesen und Schreiben geschieht über Streams, die Verbindung wird explizit über Sockets hergestellt. 1 public class SimpleNetServer { 2 public static void main ( String [] args ) throws Exception { 3 ServerSocket server = new ServerSocket (21312) ; 4 5 while ( true ) { 6 Socket client = server. accept () 7 byte [] rawdata = new byte [1024]; 8 int numread = server. getinputstream (). read ( rawdata ); 9 if( numread > 0) { 10 System. out. println ( new String ( rawdata ));

11 client. getoutputstream (). write ( rawdata ); 12 } 13 } 14 } 15 } Listing 1.1. Einfacher Server mit java.net Dieses Code-Beispiel entspricht einem minimalen Server, der mittels java.net API entwickelt wurde. Der ServerSocket lauscht auf Port 21312 und nimmt in einer Endlosschleife Verbindungen an (accept()), ließt über seinen InputStream und schreibt auf den OutputStream des verbundenen Clients. Diese Art der Netzwerkprogrammierung ist blockierend. Das heißt, dass der Server an den Stellen read(rawdata) und write(rawdata) wartet, bis das Lesen bzw. Schreiben abgeschlossen ist und währenddessen keine neue Verbindung annehmen kann. Abb. 1. Klassischer multithreaded Server Der nächste Schritt wäre nun, eine Klasse von Thread oder Runnable abzuleiten, die den Client-Socket als Parameter übernimmt und die Kommunikation in der Methode run() des Threads/Runnable-Objekts abarbeitet. Damit wäre das Problem des Blockierens zwar umgangen, jedoch skaliert diese Lösung sehr schlecht, da für jede Verbindung ein Thread erstellt wird. Dieser wartet in der Regel einen großen Teil der Zeit auf I/O und verschwendet währenddessen Speicher des Stacks. 3 Features von NIO Nachdem nun die klassiche Netzwerkprogrammierung mit Sockets beschrieben wurde, sollen nun die Vorzüge der Netzwerkprogrammierung mit NIO beschrieben werden. Vorgestellt werden das Prinzip des select()-calls inkl. der SelectionKeys (zusammengefasst als Select-Pattern ) und das Reactor-Pattern als Entwurfsmuster für Anwendungen, die die Abarbeitung der SelectionKeys quasi-verteilt vornehmen.

3.1 Select-Pattern Der Selector wird dazu verwendet, um nicht-blockierende Netzwerkprogrammierung zu ermöglichen. Das Selector-Pattern verfolgt dabei einen ereignisorientierten Ansatz. Channels, die die Verbindung zwischen den Endpunkten darstellen, werden bei einem Selektor registriert. Durch einen select()-call erhält man die Anzahl der Events, die bei allen registrierten Channels eingetreten sind. Die Abarbeitung der Events erfolgt über ein Abfragen der Events mittels selectedkeys() und dem anschließendem, iterativen bearbeiten. Hierfür bietet der Selector die Methode iterator() an. Es folgt ein Beispiel für einen einfachen Server mit Selector und ServerSocket- Channel als Basis: 1 public class SimpleNioServer { 2 3 public static void main ( String [] args ) { 4 Selector selector = Selector. open (); 5 6 ServerSocketChannel server = ServerSocketChannel. open (); 7 server. socket (). bind ( new InetSocketAddress (21312) ); 8 server. configureblocking ( false ); 9 10 while ( true ) { 11 if( selector. select () > 0) { 12 Iterator it = selector. selectedkeys (). iterator (); 13 14 while (it. hasnext ()) { 15 SelectionKey key = ( SelectionKey ) it. next (); 16 it. remove (); 17 if(key. isacceptable ()) { 18 SocketChannel client = server. accept (); 19 client. configureblocking ( false ); 20 SelectionKey clientkey = client. register ( selector, SelectionKey. OP_READ ); 21 } else if( key. isreadable ()) { 22 SocketChannel client = key. channel (); 23 ByteBuffer buffer = ByteBuffer. allocate (1024) ; 24 int numread = client. read ( buffer ); 25 System. out. println (" Client meldet : " + new String ( buffer. array ())); 26 } 27 } 28 } 29 } 30 } 31 } Listing 1.2. Einfacher Server mit java.net 3.2 Reactor-Pattern Das Reactor-Pattern stellt ein Designmuster dar, bei dem die Aufgaben der Verbindungsannahme und der Bearbeitung ausgelagert bzw. verteilt werden (Stich-

wort: dispatcher). Dieses Prinzip ähnelt damit dem AWT-Event-Dispatching [Lea12]. Der Reactor stellt dabei die zentrale Einheit der Serveranwendung dar. Bei Verbindungswünschen durch Clients an den Reactor gibt dieser die Anfragen an einen Acceptor weiter. Die Bearbeitung von darauffolgenden I/O-Operationen werden anschließend durch den Acceptor weiterdeligiert. Der schematische Aufbau eines Reactors ist in Abb. 2 zu sehen. Abb. 2. Schematischer Aufbau des Reactor-Patterns Das Reactor-Pattern verfolgt dabei den Ansatz, pro auftretendem Event einen Thread zu erstellen anstatt pro Client-Verbindung [JNet12] und arbeitet so nach dem Divide and Conquer Prinzip. Dafür verwendet die Reactor-Klasse die attach()-methode der SelectionKey-Klasse um dem SelectionKey, der bei der Registrierung des ServerSocketChannels zurückgegeben wird, eine Acceptor- Instanz als eine Art Callback übergibt. Das Reactor-Pattern erhöht dabei die Skalierbarkeit noch weiter, als der einfache Ansatz, wie er bereits oben beschrieben wird. Ein Nachteil, der sich durch die Verwendung des Reactor-Patterns ergibt, ist die Gefahr eines potenziellen Memoryleaks. Das Problem dabei wird z.b. von Jeanfrancois Arcand [Arc12] beschrieben. Es entsteht dadurch, dass das Attachement eines SelectionKeys möglicherweise für längere Zeit ungebraucht im Speicher liegt. Bei mehreren Tausend Verbindungen entsteht dabei ein immer größerer Speicherbedarf für Objekte, die nicht gebraucht werden. Dem lässt sich jedoch dadurch entgegenwirken, indem z.b. der Reactor als Singleton implementiert wird. Damit kann sichergestellt werden, dass es insgesamt nur ein Acceptor-Attachement innerhalb der Serveranwendung gibt. 4 Einsatzgebiete von Java NIO Die NIO-API kann bzw. sollte überall dort in der Netzwerkprogrammierung eingesetzt werden, wo eine hohe Anzahl an Verbindungen erwartet wird und eine hohe Skalierbarkeit benötigt wird. Die Arbeit mit NIO gestaltet sich dabei

jedoch grundsätzlich etwas komplexer als mit NET. Aus diesem Grund sind einige Frameworks am Markt zu finden, die auf NIO aufbauen. Eines dieser Frameworks ist Netty, dass urpsrünglich zu JBoss gehörte und ein asynchrones, ereignisbasiertes Framework für RAD von Netzwerkanwendungen darstellt [Nety12]. Als Beispiel ist unter http://netty.io/docs/stable/xref/org/jboss/netty/example/echo/p summary.html (letzter Zugriff: 18.05.2012) ein Echoserver zu finden, der mit Netty implementiert wurde. 5 Fazit und Ausblick NIO spielt seine Stärken, also die nicht-blockierende API und die damit verbundene, bessere Skalierbarkeit vor allem dort aus, wo in der Netzwerkprogrammierung viele hunderte oder tausenden Verbindungen gleichzeitig behandelt werden müssen. Hier stößt NET an ihre Grenzen. Zwar lassen sich mit NET auch multithreaded Anwendungen entwickeln, jedoch muss der Entwickler hier noch viel Arbeit selbst erledigen. Selector, Channels und Buffer stehen unter NIO bereits out of the box zur Verfügung und ermöglichen zwar etwas komplexere aber besser skalierende und flexiblere Lösungen. Literaturverzeichnis [Arc12] Tricks and Tips with NIO part II: Why SelectionKey.attach() is evil http://jfarcand.wordpress.com/2006/07/06/tricks-and-tips-with-niopart-ii-why-selectionkey-attach-is-evil/ Letzter Zugriff: 18.05.2012 [JNet12] Architecture of a Highly Scalable NIO-Based Server http://today.java.net/article/2007/02/08/architecture-highly-scalable-niobased-server Letzter Zugriff: 17.05.2012 [Lea12] Scalable I/O in Java, Dough Lea State University of New York at Oswego http://gee.cs.oswego.edu/dl/cpjslides/nio.pdf Letzter Zugriff: 26.04.2012 [Nety12] Netty - the Java NIO Client Server Socket Framework http://www.netty.io Letzter Zugriff: 18.05.2012