neues Packet in Java 2, JDK 1.4
bietet für Ein-/Ausgabe Operationen explizite Pufferverwaltung (für primitive Datentypen)
neues Channel Konzept für Datei- und Netzwerk IO
FileChannel bieten das Einblenden von Dateien in den Hauptspeicher als IO-Buffer und explizite fein granulare Locks für Teile der Datei
SocketChannel bieten nicht blockierende Operationen für Netzwerk Verbindungen mit mehrfachen Select-Operationen (ähnlich wie in Ada, Occam oder CSP)
Codierung und Dekodierung der Buffer bezüglich beliebiger Charaktersets
Buffer
Channel
FileLock
Selector
Beispiele
Buffer sind Container für (endliche) Folgen von Werten von primitiven Datentypen
Für jeden primitiven Datentyp gibt es eine Buffer-Klasse:
ByteBuffer
,
ShortBuffer
, CharBuffer
,
DoubleBuffer
, FloatBuffer
,
IntBuffer
, LongBuffer
die Channels verwenden nur ByteBuffer für IO, die anderen Buffer können ohne Aufwand auf ByteBuffer abgebildet werden
Buffer können im Heap der JVM angelegt werden oder
direkt von Betriebssystem verwaltet werden
ByteBuffer.allocate(capacity)
legt einen Heap Buffer an,
ByteBuffer.allocateDirect(capacity)
legt einen
Buffer im jeweiligen Betriebssystem an
es gibt sonst keine Konstruktoren
Hinzufügen von Daten zum Buffer erfolgt mit
verschiedenen put(.)
Methoden oder mit
verschiedenen Channel read(.)
Methoden
Entnahme von Daten aus dem Buffer erfolgt mit
verschiedenen get()
Methoden oder mit
verschiedenen Channel write(.)
Methoden
für das Hinzufügen oder Entfernen von Daten zum / vom Buffer
werden zwei "Zeiger" verwaltet: position
und limit
position
definiert die Stelle für die nächste
put(.)
oder get()
Operation
limit
definiert die Stelle, die für keine der
nächsten put(.)
oder get()
Operation
erreichbar sind
Information über die "Zeiger"-Stände erhält man mit:
position()
limit()
remaining()
: limit - position
capacity()
: Grösse des Buffers
die "Zeiger" werden (neben den put, get, read, write Methoden)
mit Hilfe der folgenden Methoden verwaltet:
clear()
: position=0, limit=capacity
flip()
: limit=position, position=0
rewind()
: position=0
position(neu)
: position=neu
limit(neu)
: limit=neu
ObjectBuffer Definition
private ByteBuffer bb; public ObjectBuffer(ByteBuffer bb) { this.bb = bb; }
put() Methode
public void put( Object obj ) throws IOException { ByteArrayOutputStream os = new ByteArrayOutputStream(); ObjectOutputStream oos = new ObjectOutputStream( os ); oos.writeObject( obj ); oos.close(); bb.put( os.toByteArray() ); }
get() Methode
public Object get() throws IOException, ClassNotFoundException { int pos; int rem; byte[] cont; if ( bb.hasArray() ) { cont = bb.array(); pos = bb.position(); rem = bb.remaining(); } else { // could be inefficient bb.mark(); ByteBuffer bba = ByteBuffer.allocate( bb.remaining() ); bba.clear(); bba.put( bb ); bba.rewind(); cont = bba.array(); pos = bba.position(); rem = bba.remaining(); bb.reset(); } ByteArrayInputStream is = new ByteArrayInputStream( cont, pos, rem ); int read = is.available(); ObjectInputStream ois = new ObjectInputStream( is ); Object obj = ois.readObject(); ois.close(); read = read - is.available(); bb.position( bb.position() + read ); return obj; }
Source: ObjectBuffer.java
Channel sind beliebige, gepufferte Ein-/Ausgabe-Kanäle
unter Anderem gibt es:
FileChannel
für Dateien,
SocketChannel
für TCP/IP-Verbindungen
und Pipe
Channel für Shared-Memory Verbindungen
alle Channel verfügen über verschiedene
read( byteBuffer )
Methoden,
die Daten aus dem Channel in den byteBuffer transferieren
und über verschiedene
write( byteBuffer )
Methoden,
die Daten aus dem byteBuffer in den Channel transferieren
FileChannel
können z.B.
aus File...putStream
s mit Hilfe von
getChannel()
Methoden erzeugt werden
weiter verfügen FileChannel
über verschiedene
lock()
Methoden, mit denen
der Zugriff auf spezifizierte Teile der Datei
synchronisiert werden kann
und über eine
map()
Methode, mit der Teile der Datei
direkt als ByteBuffer verwendet werden können
SocketChannel
müssen
direkt über ihre open()
Methoden
bzw. über die ServerSocketChannel
accept()
Methode erzeugt werden
über die socket()
Methode
kann (und muss ggf.) auf den darunter liegenden
TCP/IP Socket zu gegriffen werden
(z.B. um IO-Streams zu erhalten)
SocketChannel
können mit
configureBlocking(.)
in einen
nicht blockierenden Zustand versetzt werden,
in diesem Zustand werden sie mit
Selector
s (s.u.) verwaltet
Pipe
Channel besitzt die Methoden
sink()
und source()
mit denen ein schreibbarer Pipe.SinkCannel
bzw. ein lesbarer Pipe.SourceCannel
erzeugt werden kann
Pipe
Channel können auch nicht blockierend
betrieben werden
ObjectBufferChannel Definition
final ByteBuffer hbb; final ObjectBuffer ob; final SocketChannel soc; public ObjectBufferChannel(SocketChannel s, int cap) throws IOException { soc = s; hbb = ByteBuffer.allocate(cap); ob = new ObjectBuffer( hbb ); }
send() Methode
public void send(Object v) throws IOException { synchronized (hbb) { hbb.clear(); ob.put(v); hbb.flip(); soc.write( hbb ); } }
receive() Methode
public Object receive() throws IOException, ClassNotFoundException { Object v = null; synchronized (hbb) { hbb.clear(); soc.read( hbb ); hbb.flip(); v = ob.get(); } return v; }
Source: ObjectBufferChannel.java
nur zwischen verschiedenen JVMs und anderen (nicht Java) Prozessen, nicht zur Synchronisation zwischen Threads der gleichen JVM
werden direkt auf Datei-Locks des jeweiligen Betriebssystems abgebildet
Vorsicht bei Locks in Verbindung mit Memory mapped Files und Netzwerk-Dateisystemen
dient der Bündelung verschiedener Ereignisse, die bei nicht blockierenden Channels, eintreten können
ein Selector
wird mit
einer open()
Methode erzeugt
die select()
Methode blockiert bei Bedarf
bis mindestens ein Ereignis von einem registrierten Channel
eingetreten ist
die register(select,event)
Methode von
SelectableChannel
verknüpft einen Selector und
ein Ereignis und liefert einen SelectionKey
als Ereignisse sind vorgesehen:
OP_ACCEPT
bei ServerSocketChannel accept()
OP_CONNECT
bei SocketChannel connect()
OP_READ
bei SocketChannel read()
OP_WRITE
SocketChannel write()
die selectedKeys()
Methode liefert
(nach der Terminierung von select()) ein
Set
von SelectionKey
s
die Anwendung sieht das Set durch und führt bei
entsprechenden Keys die ausstehenden Channel-Operationen
zu Ende, der zugehörige Kanal lässt sich über die
channel()
Methode abfragen
SelectionKey
besitzt die Methoden
isAccepatble()
, isConnectable()
,
isReadable()
, isWritable()
zur Überprüfung des Ereignisses
die bearbeiteten Ereignisse werden aus dem Set mit
remove()
entfernt, damit nicht mehrfach das
gleiche Ereignis bearbeitet wird
mit der wakeup()
Methode von Selector
wird auch ein Terminieren der select()
Methode
erreicht
mit der cancel()
Methode von SelectionKey
wird der Channel von dem Selector deregistriert
SelectionKeys können darüber hinaus noch mittels Attachments anwendungsspezifische Daten verwalten
Die Arbeit mit nicht blockierenden Channel Operationen verläuft im Wesentlichen in folgenden 3 Schritten:
Initialisierung
private Selector selector = null; private SelectionKey serverkey = null; private ServerSocketChannel server = null; public ChannelFactoryNioBuffer(int port) { try { selector = Selector.open(); server = ServerSocketChannel.open(); server.configureBlocking(false); serverkey = server.register(selector,SelectionKey.OP_ACCEPT); server.socket().bind( new InetSocketAddress(port) ); } catch (BindException e) { serverkey.cancel(); } ... }
Behandeln der accept-Ereignisse
ObjectBufferChannel c = null; SocketChannel s; while (c == null) { selector.select(); // blocking Set<SelectionKey> keys = selector.selectedKeys(); for ( SelectionKey k: keys ) { if ( k == serverkey ) { if ( k.isAcceptable() ) { // just in case keys.remove(k); s = server.accept(); s.configureBlocking(true); c = new ObjectBufferChannel( s, capacity ); break; // ignore other keys } } else { // ignore client keys selector.wakeup(); } } }
Behandeln der connect-Ereignisse
SelectionKey clientkey = null; ObjectBufferChannel c = null; SocketChannel client; while (c == null) { client = SocketChannel.open(); client.configureBlocking(false); clientkey = client.register(selector,SelectionKey.OP_CONNECT); client.connect( new InetSocketAddress(host,port) ); selector.select(); // blocking Set<SelectionKey> keys = selector.selectedKeys(); for ( SelectionKey k: keys ) { if ( k == clientkey ) { if ( k.isConnectable() ) { // if server ready keys.remove(k); k.cancel(); // required to make blockable client = (SocketChannel) k.channel(); client.finishConnect(); client.configureBlocking(true); c = new ObjectBufferChannel( client, capacity ); break; // ignore other keys } } else { // ignore server keys selector.wakeup(); } } }
Source: ChannelFactoryNioBuffer.java
© Universität Mannheim, Rechenzentrum, 2005-2009.
Heinz KredelLast modified: Wed Sep 12 21:30:13 CEST 2009