# Endlosschleife im CompletionHandler (AIO)



## Xeonkryptos (16. Apr 2012)

Moin Community,

ich habe ein großes Problem... Ich habe angefangen, mich mit dem AIO zu beschäftigen und soweit habe ich bei deren Verwendung keine Probleme, doch tritt bei mir ein merkwürdiger Fehler auf: ich habe eine Endlosschleife, wo keine Endlosschleife sein sollte. ???:L

Zu Anfang schrieb ich für meinen Server und Client jeweils völlig eigene Klassen, die das Lesen für einkommende Nachrichten übernehmen sollten und dort klappte natürlich alles einwandfrei. Jetzt habe ich diese Klassen etwas abstrahiert und diese in Oberklassen umgewandelt, da doch diverse Methoden dasselbe getan haben und dies einfacher zu Regeln war und natürlich für den Server und den Client nochmal spezielle Klassen, die von der zusammengefassten Oberklasse erben und erweiterten, doch genau hier beginnt mein Problem, so wie es den Anschein hat...

Jedesmal, wenn eine Nachricht empfangen wird, egal ob Client oder Server, landet dieser im CompletionHandler der Oberklasse in einer Endlosschleife und teilt mir mit (über die Konsole ausgegeben), dass er durchgehend neue Nachrichten empfängt, obwohl ich nachweisen kann, dass der Server oder Client nur eine einzelne Nachricht gesendet haben. ???:L:autsch:


Lesende Oberklasse:

```
public class AioReaderBase implements CompletionHandler<Integer, Object> {

	private ByteBuffer readBuffer;
	private final AsynchronousSocketChannel socketChannel;

	/**
	 * 
	 */
	protected byte[] messageBytes;

	/**
	 * 
	 * @param socketChannel
	 * @throws IOException
	 * @throws InterruptedException
	 * @throws ExecutionException
	 */
	public AioReaderBase(AsynchronousSocketChannel socketChannel)
			throws IOException, InterruptedException, ExecutionException {
		setReadBuffer(ByteBuffer.allocate(10 * 1024));
		this.socketChannel = socketChannel;
	}

	@Override
	public void completed(Integer result, Object attachment) {
		read();
		socketChannel.read(readBuffer, null, this);
	}

	/**
	 * 
	 */
	protected void read() {
		messageBytes = new byte[readBuffer.remaining()];
		readBuffer.get(messageBytes);
	}

	@Override
	public void failed(Throwable exc, Object attachment) {
		// ...
	}

	/**
	 * 
	 * @return
	 */
	public AsynchronousSocketChannel getSocketChannel() {
		return socketChannel;
	}

	/**
	 * 
	 * @return
	 */
	public ByteBuffer getReadBuffer() {
		return readBuffer;
	}

	/**
	 * @param readBuffer
	 *            the readBuffer to set
	 */
	public void setReadBuffer(ByteBuffer readBuffer) {
		this.readBuffer = readBuffer;
	}
}
```


Klasse Reader erbt von AioReaderBase und erweitert diese um unwichtige Methoden, die ich für mein Programm brauche, aber keinen Einfluss auf die Streams oder das Lesen selbst nehmen.

Die Klasse ServerReader übernimmt die spezifische Bearbeitung einkommender Nachrichten.

```
public class ServerReader extends Reader {

	private Server client;

	/**
	 * 
	 * @param socketChannel
	 * @param server
	 * @throws InterruptedException
	 * @throws ExecutionException
	 * @throws IOException
	 */
	public ServerReader(AsynchronousSocketChannel socketChannel, Server server)
			throws InterruptedException, ExecutionException, IOException {
		super(socketChannel);
		setServer(server);
	}

	private void processReading() throws InterruptedException,
			ExecutionException {
		System.out.println("Lese Nachricht.");
		String msg = new String(messageBytes); // Einlesen der Nachricht
	}

	private void setServer(Server server) {
		if (server == null)
			throw new IllegalArgumentException("server == null");
		this.client = server;
	}

	/**
	 * 
	 * @return
	 */
	protected Server getClientConn() {
		return client;
	}

	@Override
	protected void read() {
		try {
			super.read();
			processReading();
		} catch (InterruptedException e) {
			e.printStackTrace();
		} catch (ExecutionException e) {
			e.printStackTrace();
		}
	}
}
```

Übernimmt das Lesen vom Client

```
public class ClientReader extends Reader {

	private final int serializerPort = 4000;

	/**
	 * 
	 * @param socketChannel
	 * @param user
	 * @param host
	 * @throws IOException
	 * @throws InterruptedException
	 * @throws ExecutionException
	 */
	public ClientReader(AsynchronousSocketChannel socketChannel, User user,
			String host) throws IOException,
			InterruptedException, ExecutionException {
		super(socketChannel, user);
	}

	@Override
	protected void read() {
		super.read();
		String msg = new String(messageBytes);
                // Bearbeitung der empfangenen Nachricht
	}
}
```

Server

```
public class Server extends AioServerBase {

	private static final int serverPort = 3050, readerPort = 4000;

	/**
	 * Erstellt einen AioServer, der die Verbindung zum Server freischaltet und
	 * die Clients daraufhin registriert.
	 * 
	 * @throws IOException
	 *             Bei einem IO-Fehler
	 */
	public Server() throws IOException {
		super(serverPort);
	}

	/**
	 * 
	 * @param msg
	 *            Zu versendende Nachricht
	 * @throws InterruptedException
	 *             Fehler beim Schreiben
	 * @throws ExecutionException
	 *             Fehler beim Schreiben
	 */
	public void send(String msg) throws InterruptedException,
			ExecutionException {
		System.out.println("Schicke Nachricht an Client.");
		synchronized (database) {
			// Senden der Nachricht mit der normalen write-Methode
		}
	}

	@Override
	public void completed(AsynchronousSocketChannel result, Object attachment) {
		try {
			AioReaderBase reader = new ServerReader(result, this);
			result.read(reader.getReadBuffer(), null, reader);
			client.accept(null, this);
		} catch (InterruptedException e) {
			e.printStackTrace();
		} catch (ExecutionException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}
	}
}
```


Client

```
public class Client extends AioClientBase {

	private final String host;

	public Client(User user, String host, int port)
			throws IOException {
		super(host, port);
		this.host = host;
	}

	@Override
	public void completed(Void result, Object attachment) {
		try {
			reader = new ClientReader(server, host);
			server.read(reader.getReadBuffer(), null, reader);
		} catch (IOException e) {
			e.printStackTrace();
		} catch (InterruptedException e) {
			e.printStackTrace();
		} catch (ExecutionException e) {
			e.printStackTrace();
		}
	}

	@Override
	public void failed(Throwable exc, Object attachment) {
	}
}
```

Hier ist auch erstmal der Code auf das wesentliche reduziert bzw auch wegkommentiert.

Nun ist mein Problem, dass die read-Methode der spezifischen Reader-Klassen in einer Endlosschleife aufgerufen werden und ich weiß nicht wieso. Diese wird nur aufgerufen, wenn der Client oder Server erfolgreich eine Nachricht erhalten und dann diese nach meinen Vorgaben abarbeiten, aber dies funktioniert nicht wie erwünscht...

Hoffe, ihr könnt mir bei der Lösung meines Problems helfen.


----------



## SlaterB (17. Apr 2012)

ich kenne AIO nicht, an deinem Code fällt mir nichts direkt auf, aber mal zur systematischen Untersuchung:
eine richtige Schleife ist ja nicht zu sehen, was passiert stattdessen, was meinst du genau?

rufen sich Methoden ständig gegenseitig auf? dann sollte es doch recht bald eine StackOverflowException geben,
kommt es dazu oder passiert das bisher relativ langsam?

schwieriger wäre der Fall ständig wiederholter Aufrufe deiner Methoden von einer gewissen unbekannten Basis aus, 
dort die Schleife vermutet, die eigentlich aufhören sollten wenn etwa die Nachricht einmal verarbeitet ist?

was genau passiert denn nun, wo stellst du auf welche Weise exakt welches Fehlverhalten fest?
gib in jedem Fall an dieser Stelle 
new Error().printStackTrace();
aus und poste das hier,
was kannst du über die Aufrufer sagen, sind das nur API-Klassen oder sind irgendwo auch welche von dir beteiligt
(abgesehen von den obersten, der direkten Bearbeitung)?


----------



## Xeonkryptos (17. Apr 2012)

SlaterB hat gesagt.:


> ich kenne AIO nicht, an deinem Code fällt mir nichts direkt auf, aber mal zur systematischen Untersuchung:
> eine richtige Schleife ist ja nicht zu sehen, was passiert stattdessen, was meinst du genau?



Es wird auch im AIO keine Schleifen direkt benötigt. Mein Problem ist, dass meine read-Methode aus der Klasse AioReaderBase, die ich für die spezielle Bearbeitung eingehender Nachrichten natürlich überschreibe, jedes mal erneut aufgerufen wird, obwohl dies ein Fehlverhalten ist. Um genauer zu sein, wird die implementierte completed-Methode der Klasse CompletionHandler<Integer, Object>  immer wieder neu aufgerufen und damit auch meine read-Methode.

Im Prinzip funktioniert das AIO wie NIO und zwar non-blocking, nur dass hier die Bearbeitung der Verbindung, das Erhalten von Nachrichten, an das Betriebssystem weitergegeben wird und dieser die JVM und damit mein Programm (CompletionHandler<Integer, Object>) benachrichtigt, es ist eine neue Nachricht da, führe jetzt die Methode completed aus: Prinzip der Listener mit der Ausnahme, dass nach einmaligen Lesen dieser immer wieder neu sich "registrieren" muss oder anderst gesagt, er muss dem OS immer wieder erneut sagen, dass er die nächste einkommende Nachricht wieder erhalten möchte und das immer wieder von neuem.



SlaterB hat gesagt.:


> rufen sich Methoden ständig gegenseitig auf? dann sollte es doch recht bald eine StackOverflowException geben,
> kommt es dazu oder passiert das bisher relativ langsam?



Es kommt zu keiner StackOverFlowException... Während ich hier diesen Text verfasse, warte ich darauf, dass dies passiert, aber es überlastet nicht die JVM und ihren Speicher! Beschreibung, wieso dass den Speicher nicht überlastet steht oben.



SlaterB hat gesagt.:


> schwieriger wäre der Fall ständig wiederholter Aufrufe deiner Methoden von einer gewissen unbekannten Basis aus,
> dort die Schleife vermutet, die eigentlich aufhören sollten wenn etwa die Nachricht einmal verarbeitet ist?



Das trifft genau den Punkt! Die Methode completed, implementiert in AioReaderBase, sollte nur 1x aufgerufen werden, WENN eine Nachricht erhalten wird, doch hier beginnt plötzlich eine Art Endlosschleife: Die Methode wird immer wieder von neuem aufgerufen, obwohl keine weiteren Nachrichten verschickt werden, die bearbeitet werden könnten/müssten.



SlaterB hat gesagt.:


> was genau passiert denn nun, wo stellst du auf welche Weise exakt welches Fehlverhalten fest?
> gib in jedem Fall an dieser Stelle
> new Error().printStackTrace();
> aus und poste das hier,
> ...




```
java.lang.Error
	at util.aio.AioReaderBase.completed(AioReaderBase.java:40)
	at util.aio.AioReaderBase.completed(AioReaderBase.java:1)
	at sun.nio.ch.Invoker.invokeUnchecked(Unknown Source)
	at sun.nio.ch.Invoker.invokeDirect(Unknown Source)
	at sun.nio.ch.Invoker.invoke(Unknown Source)
	at sun.nio.ch.AsynchronousSocketChannelImpl.read(Unknown Source)
	at sun.nio.ch.AsynchronousSocketChannelImpl.read(Unknown Source)
	at java.nio.channels.AsynchronousSocketChannel.read(Unknown Source)
	at util.aio.AioReaderBase.completed(AioReaderBase.java:43)
	at util.aio.AioReaderBase.completed(AioReaderBase.java:1)
	at sun.nio.ch.Invoker.invokeUnchecked(Unknown Source)
	at sun.nio.ch.Invoker.invokeUnchecked(Unknown Source)
	at sun.nio.ch.WindowsAsynchronousSocketChannelImpl$ReadTask.completed(Unknown Source)
	at sun.nio.ch.Iocp$EventHandlerTask.run(Unknown Source)
	at sun.nio.ch.AsynchronousChannelGroupImpl$1.run(Unknown Source)
	at java.util.concurrent.ThreadPoolExecutor.runWorker(Unknown Source)
	at java.util.concurrent.ThreadPoolExecutor$Worker.run(Unknown Source)
	at java.lang.Thread.run(Unknown Source)
```

Hier der gewünschte StackTrace. Die Zeile 40 aus der AioReaderBase ist die Stelle, an der ich den Error erzeugt habe und Zeile 43 ist die Stelle, an der ich 
	
	
	
	





```
socketChannel.read(readBuffer, null, this);
```
 aufrufe, damit ein neues Lesen der Nachrichten initiiert wird.

API-Klassen sind beteiligt: Ich verwende überwiegend die Klasse [JAPI]AsynchronousSocketChannel[/JAPI], [JAPI]AsynchronousServerSocketChannel[/JAPI], [JAPI]CompletionHandler[/JAPI], [JAPI]ByteBuffer[/JAPI].

AsynchronousSocketChannel entspricht einem Socket.
AsynchronousServerSocketChannel entspricht einem ServerSocket.

CompletionHandler (Interface) verwende ich beim Verbindungsaufbau und erhalten von Nachrichten, damit ich gezielt unterscheiden kann, ob das jeweile gelungen ist (completed-Methode aufrufen) oder misslungen ist (failed-Methode aufrufen). Andere Möglichkeit wäre über ein Future-Objekt, doch da müsste ich ggf. mein Programm wieder blockieren, wenn der nachfolgende Code auf das Ergebnis aufbaut und genau das will ich vermeiden!

ByteBuffer dient einfach als Verpackung, der meine Bytes entgegennimmt oder verschickt.


----------



## SlaterB (17. Apr 2012)

dass AioReaderBase zweimal auftaucht verwirrt mich jetzt etwas, 
zuerst kommt Zeile 43 dran wie du sagst, 
wenn du dort nichts machst, jedenfalls nicht sowas verdächtiges wie socketChannel.read(), dann ist es zu Ende?
ist diese Zeile 43 schon Verarbeitung oder als Senden, als Ursache zu verstehen?

wenn du socketChannel.read() aufrufst, kommt irgendwann Zeile 40 dran, das verstehe ich, 
AioReaderBase ist ja Basisklasse, sind in diesem StackTrace 43 und 40 dieselbe Klasse, dasselbe Objekt,
oder ist der eine Server und der andere Client oder andersrum?
vielleicht könnte ich alles selber erkennen wenn ich noch genauer schaue, aber das sind eben die objektiven Fragen von außen 

auch noch unklar: wie weit reicht die Wiederholung,
kommt einmal Zeile 43 dran (dort loggen) und geht es dann beliebig oft zu Zeile 40,
oder wird beides ständig wiederholt?

ich frage übrigens wie so oft ohne echte Hoffnung dass ich was lösen kann, auch wenn es dann doch oft noch klappt,
aber nicht zu viel erwarten


----------



## Xeonkryptos (17. Apr 2012)

SlaterB hat gesagt.:


> dass AioReaderBase zweimal auftaucht verwirrt mich jetzt etwas,
> zuerst kommt Zeile 43 dran wie du sagst,
> wenn du dort nichts machst, jedenfalls nicht sowas verdächtiges wie socketChannel.read(), dann ist es zu Ende?
> ist diese Zeile 43 schon Verarbeitung oder als Senden, als Ursache zu verstehen?



Die Zeile 43 bzw socketChannel.read(...) ist der Befehl an das Betriebssystem, die nächste einkommende Nachricht an mein Programm weiterzuleiten gleichzusetzen mit dem Registrieren eines Listeners, der dann nur noch auf sein Einsatz wartet und diese Zeile muss ich nach jedem neuen "Event" neu aufrufen, da ansonsten nichts mehr gelesen wird, zu vergleichen mit dem Verhalten von normalen Sockets und deren read-Methode: ohne neuen Aufruf passiert nichts.



SlaterB hat gesagt.:


> wenn du socketChannel.read() aufrufst, kommt irgendwann Zeile 40 dran, das verstehe ich,
> AioReaderBase ist ja Basisklasse, sind in diesem StackTrace 43 und 40 dieselbe Klasse, dasselbe Objekt,
> oder ist der eine Server und der andere Client oder andersrum?



Beide Client und Server verwenden in gewisserweise die AioReaderBase-Klasse. Der Client hat eine eigene Klasse, die von AioReaderBase erbt und dann die read-Methode überschreibt für seine eigenen Zwecke, sowie es der Server auch macht. Beide, Client und Server, verwenden diese Klasse in gewisserweise, nur auf ihre eigene Weise durch das Überschreiben spezieller Methoden. Würde sonst wenig Sinn machen. 

Dadurch ergibt sich auch, dass im Server und im Client derselbe Fehler auftritt, weil ich es in der Basisklasse implementiert habe:

[JAVA=38]	@Override
	public void completed(Integer result, Object attachment) {
		new Error().printStackTrace();
		read();
		// setReadBuffer(ByteBuffer.allocate(10 * 1024));
		socketChannel.read(readBuffer, null, this);
	}[/code]

Hier noch einmal die implementierte Methode mit der verlangten Fehlerausgabe aus der AioReaderBase-Klasse, damit es verständlicher ist.
BTW: Der auskommentierte Methodenaufruf 
	
	
	
	





```
setReadBuffer(ByteBuffer.allocate(10 * 1024));
```
 sollte eigentlich ein Test meinerseits werden, da man den Inhalt eines ByteBuffers nicht direkt löschen kann und ich dann einfach immer einen neuen erstellen wollte. ;-) Gehört aber hier nicht zu. 



SlaterB hat gesagt.:


> auch noch unklar: wie weit reicht die Wiederholung,
> kommt einmal Zeile 43 dran (dort loggen) und geht es dann beliebig oft zu Zeile 40,
> oder wird beides ständig wiederholt?



Die Zeile 43 wird öfters wiederholt bis dann die Zeile 40 wieder geloggt wird.


```
java.lang.Error
	at util.aio.AioReaderBase.completed(AioReaderBase.java:40)
	at util.aio.AioReaderBase.completed(AioReaderBase.java:1)
	at sun.nio.ch.Invoker.invokeUnchecked(Unknown Source)
	at sun.nio.ch.Invoker.invokeDirect(Unknown Source)
	at sun.nio.ch.Invoker.invoke(Unknown Source)
	at sun.nio.ch.AsynchronousSocketChannelImpl.read(Unknown Source)
	at sun.nio.ch.AsynchronousSocketChannelImpl.read(Unknown Source)
	at java.nio.channels.AsynchronousSocketChannel.read(Unknown Source)
	at util.aio.AioReaderBase.completed(AioReaderBase.java:43)
	at util.aio.AioReaderBase.completed(AioReaderBase.java:1)
	at sun.nio.ch.Invoker.invokeUnchecked(Unknown Source)
	at sun.nio.ch.Invoker.invokeDirect(Unknown Source)
	at sun.nio.ch.Invoker.invoke(Unknown Source)
	at sun.nio.ch.AsynchronousSocketChannelImpl.read(Unknown Source)
	at sun.nio.ch.AsynchronousSocketChannelImpl.read(Unknown Source)
	at java.nio.channels.AsynchronousSocketChannel.read(Unknown Source)
	at util.aio.AioReaderBase.completed(AioReaderBase.java:43)
	at util.aio.AioReaderBase.completed(AioReaderBase.java:1)
	at sun.nio.ch.Invoker.invokeUnchecked(Unknown Source)
	at sun.nio.ch.Invoker.invokeDirect(Unknown Source)
	at sun.nio.ch.Invoker.invoke(Unknown Source)
	at sun.nio.ch.AsynchronousSocketChannelImpl.read(Unknown Source)
	at sun.nio.ch.AsynchronousSocketChannelImpl.read(Unknown Source)
	at java.nio.channels.AsynchronousSocketChannel.read(Unknown Source)
	at util.aio.AioReaderBase.completed(AioReaderBase.java:43)
	at util.aio.AioReaderBase.completed(AioReaderBase.java:1)
	at sun.nio.ch.Invoker.invokeUnchecked(Unknown Source)
	at sun.nio.ch.Invoker.invokeDirect(Unknown Source)
	at sun.nio.ch.Invoker.invoke(Unknown Source)
	at sun.nio.ch.AsynchronousSocketChannelImpl.read(Unknown Source)
	at sun.nio.ch.AsynchronousSocketChannelImpl.read(Unknown Source)
	at java.nio.channels.AsynchronousSocketChannel.read(Unknown Source)
	at util.aio.AioReaderBase.completed(AioReaderBase.java:43)
	at util.aio.AioReaderBase.completed(AioReaderBase.java:1)
	at sun.nio.ch.Invoker.invokeUnchecked(Unknown Source)
	at sun.nio.ch.Invoker.invokeDirect(Unknown Source)
	at sun.nio.ch.Invoker.invoke(Unknown Source)
	at sun.nio.ch.AsynchronousSocketChannelImpl.read(Unknown Source)
	at sun.nio.ch.AsynchronousSocketChannelImpl.read(Unknown Source)
	at java.nio.channels.AsynchronousSocketChannel.read(Unknown Source)
	at util.aio.AioReaderBase.completed(AioReaderBase.java:43)
	at util.aio.AioReaderBase.completed(AioReaderBase.java:1)
	at sun.nio.ch.Invoker.invokeUnchecked(Unknown Source)
	at sun.nio.ch.Invoker.invokeDirect(Unknown Source)
	at sun.nio.ch.Invoker.invoke(Unknown Source)
	at sun.nio.ch.AsynchronousSocketChannelImpl.read(Unknown Source)
	at sun.nio.ch.AsynchronousSocketChannelImpl.read(Unknown Source)
	at java.nio.channels.AsynchronousSocketChannel.read(Unknown Source)
	at util.aio.AioReaderBase.completed(AioReaderBase.java:43)
	at util.aio.AioReaderBase.completed(AioReaderBase.java:1)
	at sun.nio.ch.Invoker.invokeUnchecked(Unknown Source)
	at sun.nio.ch.Invoker.invokeDirect(Unknown Source)
	at sun.nio.ch.Invoker.invoke(Unknown Source)
	at sun.nio.ch.AsynchronousSocketChannelImpl.read(Unknown Source)
	at sun.nio.ch.AsynchronousSocketChannelImpl.read(Unknown Source)
	at java.nio.channels.AsynchronousSocketChannel.read(Unknown Source)
	at util.aio.AioReaderBase.completed(AioReaderBase.java:43)
	at util.aio.AioReaderBase.completed(AioReaderBase.java:1)
	at sun.nio.ch.Invoker.invokeUnchecked(Unknown Source)
	at sun.nio.ch.Invoker$2.run(Unknown Source)
	at sun.nio.ch.AsynchronousChannelGroupImpl$1.run(Unknown Source)
	at java.util.concurrent.ThreadPoolExecutor.runWorker(Unknown Source)
	at java.util.concurrent.ThreadPoolExecutor$Worker.run(Unknown Source)
	at java.lang.Thread.run(Unknown Source)
```

Davor und danach wird mein 
	
	
	
	





```
System.out.println("Lese Nachricht");
```
 aus der überschriebenen read-Methode der Klasse ServerReader aufgerufen und das wiederholt sich endlos ohne StackOverflowException...



SlaterB hat gesagt.:


> ich frage übrigens wie so oft ohne echte Hoffnung dass ich was lösen kann, auch wenn es dann doch oft noch klappt,
> aber nicht zu viel erwarten



Frag ruhig.  Versuch macht klug und vielleicht hilft es anderen meinen Fehler zu sehen, auch wenn wir ihn nicht sehen. ;-)


----------



## SlaterB (18. Apr 2012)

dein neuer StackTrace sieht nun nach endlosen Kreis von Methodenaufrufen aus,
65 Zeilen im Stack, wächst das nicht ständig um weitere 8 Zeilen pro Runde

```
at java.nio.channels.AsynchronousSocketChannel.read(Unknown Source)
    at util.aio.AioReaderBase.completed(AioReaderBase.java:43)
    at util.aio.AioReaderBase.completed(AioReaderBase.java:1)
    at sun.nio.ch.Invoker.invokeUnchecked(Unknown Source)
    at sun.nio.ch.Invoker.invokeDirect(Unknown Source)
    at sun.nio.ch.Invoker.invoke(Unknown Source)
    at sun.nio.ch.AsynchronousSocketChannelImpl.read(Unknown Source)
    at sun.nio.ch.AsynchronousSocketChannelImpl.read(Unknown Source)
```
bis hin zu einer StackOverflowException? 
von meiner urspünglichen Unterscheidung sieht das jetzt wieder nach der komplett anderen Richtung aus..

-----

ich habe bisschen im Internet geschaut aber nicht wirklich ein komplettes Beispiel irgendwo gesehen,
hast du eins oder ist das ansonsten wirklich eine sinnvolle Beschäftigung?

----

allgemein auf den Code geschaut, wie ich wirklich auch schon am Anfang hätte machen können:

beim Überschreiben muss man die Signatur der Oberklassen exakt einhalten,


```
@Override
    public void completed(AsynchronousSocketChannel result, Object attachment) {
```
überschreibt NICHT

```
public void completed(Integer result, Object attachment) {
```
in AioReaderBase,
die Annotation hast du dir wohl selber ausgedacht, wenn das keine IDE prüft und als korrekt oder falsch bewertet kann man das gleich ganz weglassen, könnte ja wie hier falsch sein,

du musst schon auch Integer + Object nehmen, ein AsynchronousSocketChannel kann da gar nicht ankommen,
wahrscheinlich ist es wohl eher dein Zeil, AioReaderBase generisch zu machen,
wenn dann die Subklassen die generischen Parameter, etwa AsynchronousSocketChannel  + Object angeben, dann geht auch so ein completed(),

wofür brauchst du in AioReaderBase eigentlich überhaupt ein completed(), wenn das doch theoretisch von den Unterklassen überschrieben wird und diese kein super.completed() aufrufen?
die Methode, die bisher alle Probleme überhaupt macht, scheint eigentlich gar nicht drankommen zu sollen,
oder gibt es nicht Server- und Client-Objekten, die von AioReaderBase erben, noch andere Objekte direkt der Klasse AioReaderBase?

falls die Methode wiederum weg oder zumindest nicht drankommen soll, auf jeden Fall nicht direkt innerhalb des Client- oder Server-Objektes,
was ist dann eigentlich der Zweck des bisherigen Codes dort?

bisschen habe ich versucht in deinen Postings nachzulesen, aber die sind fast zu lange, was für mich ungewöhnlich ist,
und ich habe leider eher das Gefühl als weißt du kaum mehr als ich was da überhaupt passiert/ passieren soll,

-------

eine allgemeine Sichtweise gebe noch zum besten:
Listener, Handler usw. reagieren normalerweise nur auf fertig angekommende Nachrichten usw.,
das Ergebnis steht in den Parametern,
es ist NICHT ihre Aufgabe
- erst noch mit ByteBuffer usw. irgendwelche Daten aus einem Stream zu lesen, sondern das sollte für diese Nachricht schon
allgemein geschehen sein, das Ergebnis ist der Parameter, attachment oder was auch immer
- und ganz besonders müssen sie sich nicht darum kümmern, dass etwa für die nächste Nachricht wieder irgendjemand am Socket liest, falls du das mit deinem Code ausdrücken willst

so sieht es in etwa aus, das erneute read solltest du einfach streichen, denn das wirkt mehr oder weniger wie eine neue Nachricht, mit neuen Handler-Aufruf usw, Endloskreis

durch eine Server-Nachricht oder was auch immer wird 1x completed() ausgeführt,
bisher durch falsche Vererbung, Registrierung eines falschen Listeners kannst du vielleicht nicht ideal verarbeiten,
arbeite daran, aber nicht mit irgendwelchen Buffern oder read-Aufrufen

vielleicht musst du auch selber read() aufrufen, in der Tat, aber wenn dann macht das ein normaler Thread irgendwo,
NICHT der CompletionHandler


----------



## Xeonkryptos (18. Apr 2012)

SlaterB hat gesagt.:


> dein neuer StackTrace sieht nun nach endlosen Kreis von Methodenaufrufen aus,
> 65 Zeilen im Stack, wächst das nicht ständig um weitere 8 Zeilen pro Runde
> 
> ```
> ...



Nein, da kommt keine StackOverflowException.



SlaterB hat gesagt.:


> ich habe bisschen im Internet geschaut aber nicht wirklich ein komplettes Beispiel irgendwo gesehen,
> hast du eins oder ist das ansonsten wirklich eine sinnvolle Beschäftigung?
> 
> 
> ...


----------



## SlaterB (18. Apr 2012)

das mit Server/ Client/ AioReaderBase habe ich wirklich schön durcheinandergebracht,
gibt dein erstes Posting aber auch wunderbar her, wenn du (neben den Readern) quasi nur diese drei Klassen als CompletionHandler postest und von

> Zu Anfang schrieb ich für meinen Server und Client jeweils völlig eigene Klassen,

> Jetzt habe ich [..] für den Server und den Client nochmal spezielle Klassen, 
> die von der zusammengefassten Oberklasse erben und erweiterten

> Jedesmal, wenn eine Nachricht empfangen wird, egal ob Client oder Server, landet dieser im 
> CompletionHandler der Oberklasse in einer Endlosschleife 

sprichst und AioReaderBase die gepostete vermeintliche Oberklasse (ist ja ein Begriff aus Verberung)  ist, 
die Klasse die im StackTrace auftaucht, die um die es die ganze Zeit geht, 

nicht dass ich das wirklich alles im Kopf hatte, 
aber jetzt nochmal nachgeschaut, erstaunlich, nur daraus hätte ich im Leben nicht gedacht dass es noch weitere Oberklassen gibt 

--------

desweiteren fürchte ich dass mein Verständnis-Stand nach falschen Zwischenhoch nun wieder tief steht,
falls du das funktionierende Programm mit getrennten Client-/Server-Klassen hast, könnte ich daran vielleicht etwas erkennen


-----

wie groß ist eigentlich der Buffer, hast du schon angeschaut was dort drinsteht?
kann es nicht auch ganz normaler Ablauf sein, eine lange Nachricht füllt den Buffer 20x?
edit: 10 * 1024, ok, ist der Buffer dann auch so groß oder zufällig irgendein anderer?, was steht drin?

sollte der Buffer vielleicht geleert werden vor neuen read-Aufruf? 
passiert vielleicht schon, wäre sonst schöne Erklärung dafür dass eine weitere Nachricht angenommen wird,
wenn auch nicht sehr schlau von der Socket-Implementierung


----------



## SlaterB (18. Apr 2012)

siehe auch meine edits im letzten Posting,

ich lehne mich nochmal aus dem Fenster und zeige vielleicht nur meine Unkenntnis, aber bist du dir mit dem ByteBuffer-Umgang sicher?
für mich eine eher unbekannte Klasse, hier ein Beispiel-Programm:

```
public class Test
{
    public static void main(final String[] args)
        throws Exception
    {
        ByteBuffer b = ByteBuffer.allocate(1000);
        System.out.println(b.remaining() + ", " + b.position());
        byte[] a = new byte[600];
        b.put(a, 0, a.length);
        System.out.println(b.remaining() + ", " + b.position());
        b.get(a);
        System.out.println(b.remaining() + ", " + b.position());
    }
}
```
erste Ausgabe ist 
> 1000, 0
1000 Bytes noch bis zum Ende des Buffers, von Position 0 aus

dann werden hoffentlich 600 Bytes reingeschrieben, 
und die zweite Ausgabe ist 
> 400, 600
400 Bytes noch bis zum Ende des Buffers, von Position 600 aus

jetzt von Position 600 aus ein 600er byte[] lesen zu wollen endet hier in einer Exception

------

in deinem Code hast du 

```
protected void read() {
        messageBytes = new byte[readBuffer.remaining()];
        readBuffer.get(messageBytes);
    }
```
stehen, ist das korrekt? müsste man nicht eher die position() anschauen, siehe mein Beispiel?
außerdem zum Anfang des Buffers gehen?

was passiert bei dir konkret, was ist bei allen Lesevorgängen remaining(), position(), 
welche Bytes stehen im Array, was wurde gesendet?

zu meinem clear-Vorschlag:
muss die Position auf 0 gesetzt werden (reset() ) vor erneuten read()-Aufruf am Socket?


----------



## Xeonkryptos (18. Apr 2012)

SlaterB hat gesagt.:


> siehe auch meine edits im letzten Posting,
> 
> ich lehne mich nochmal aus dem Fenster und zeige vielleicht nur meine Unkenntnis, aber bist du dir mit dem ByteBuffer-Umgang sicher?



Kurz gesagt: Nicht so wirklich. Ich wollte erstmal die Verbindung sicher hinbauen, bevor ich dann weiter mit dem ByteBuffer beschäftige und zu Anfang, nach meinem ersten funktionierenden Versuch hab ich dann erst angefangen ein bisschen mit diesem herumzutesten und bin noch auf keinen grünen Zweig gekommen, dann beschloss ich aber erstmal wieder in der Hoffnung, dass ich meine Redundanzen entfernen kann, alles zu abstrahieren ohne einen späteren Fehler doch nun bin ich hier und habe einen Fehler. 

Es kann sein, dass ich mit dem ByteBuffer falsch umgegangen bin und das gestehe ich auch gerne ein, aber man versucht immer das eine zum Laufen zu bekommen, bevor man sich an das andere herantraut. 

Außerdem hab ich die ByteBuffer-Größe einfach Mal individuell groß bestimmt, da ich nie weiß, welche Größen jetzt ankommen und ich noch nichts gefunden hab, wie ich das nach dem Erhalt direkt bestimmen kann, um die Größe immer wieder individuell zu bestimmen. Außerdem habe ich mit dem Auslesen auch noch meine Probleme gehabt, das für mich wie schon erwähnt, an zweiter Stelle steht.

[EDIT]Habe jetzt auch Mal geschaut, was an Bytes gelesen werden, wenn die completed-Methode aufgerufen wird und da ist beim ersten Aufruf die Größe auf 29, so wie es auch sein soll, abzulesen, aber danach kommen immer nur 0 Bytes rein und genau das soll nicht passieren...[/EDIT]

[EDIT]Wenn ich demnächst mehr Zeit habe, bastle ich ein funktionierendes KSKB, damit du dich besser reindenken kannst, da ich die alten Klassen überschrieben bzw gelöscht habe... Werde sie aber schlank und rank halten. [/EDIT]


----------



## SlaterB (18. Apr 2012)

> aber danach kommen immer nur 0 Bytes rein und genau das soll nicht passieren...
heißt das dass dann die Position 0 ist oder bei 29 verbleibt?
wie gesagt ist ja eine interessante Theorie, dass der Buffer nicht auf leer gesetzt wird und deswegen gleich als weitere Nachricht verarbeitet wird,


----------



## Xeonkryptos (18. Apr 2012)

SlaterB hat gesagt.:


> heißt das dass dann die Position 0 ist oder bei 29 verbleibt?
> wie gesagt ist ja eine interessante Theorie, dass der Buffer nicht auf leer gesetzt wird und deswegen gleich als weitere Nachricht verarbeitet wird,



Diesen Wert kann man aus dem CompletionHandler entnehmen, der so deklariert ist: CompletionHandler<Integer, Object>. Der Integer-Wert entspricht der empfangenen Größe und diese hab ich aus der Methode completed entnommen und gepostet! Unabhängig vom ByteBuffer.


----------



## Xeonkryptos (19. Apr 2012)

Nachdem ich jetzt ein KSKB zusammengebastelt habe und es teste, damit es auch funktioniert, habe ich wohl meinen Fehler/das Problem/die Quelle gefunden. Es scheint, dass die Methode remaining() aus der ByteBuffer-Klasse dafür verantwortlich war.

Ich habe jetzt versucht, den ByteBuffer auf eine andere Art und Weise auszulesen, doch nur kenne ich keinen guten Ansatz und probiere herum... Wenn ich, wie in deinem Beispiel anstatt der put-Methode die get-Methode nehme, in der ich die zu lesenden Bytes bestimmen kann, spuckt mir mein Programm 0er bytes aus, während vor dem Senden mir richtige Byte-Zahlen ausgegeben werden...

[EDIT]Daraus lässt sich natürlich kein String herauskonvertieren, was es eigentlich machen sollte...[/EDIT]


----------



## SlaterB (19. Apr 2012)

soweit ich das sehe, musst du nicht remaining() abfragen, sondern position() für 29 oder so,
ein entsprechend großes Array anlegen, den ByteBuffer auf Position 0 setzen (reset) und dann das Array auslesen,
so wird von 0-29 gelesen,
danach nochmal reset() um wieder auf Anfang zu stellen

edit:
eher clear() statt reset()
interessanter Kommentar bei der Methode:


> Clears this buffer. The position is set to zero, the limit is set to the capacity, and the mark is discarded.
> 
> Invoke this method before using a sequence of *channel-read* or put operations to fill this buffer. For example:
> 
> ...


----------



## Xeonkryptos (19. Apr 2012)

SlaterB hat gesagt.:


> soweit ich das sehe, musst du nicht remaining() abfragen, sondern position() für 29 oder so,
> ein entsprechend großes Array anlegen, den ByteBuffer auf Position 0 setzen (reset) und dann das Array auslesen,
> so wird von 0-29 gelesen,
> danach nochmal reset() um wieder auf Anfang zu stellen
> ...



Die Position brauch ich nicht. Ich habe jetzt natürlich einen Fehler in meinem Code gehabt... Der Server, der die Nachricht wieder an den Client hat senden sollen, hat natürlich nicht den Buffer selbst ausgelesen sondern einfach ein initialisiertes, aber nicht gefülltes ByteArray ausgelesen und zurückgeschickt... Das war jetzt wohl mein Fehler. 

Zum initialisieren des ByteArrays nutze ich einfach die mir mitgeteilte ByteAnzahl durch das Integer result der completed-Methode, damit ich ganz sicher bin und auslesen kann ich es dann mit der get-Methode, die mir die Bytes ausliest passend zur Größe des Arrays, welche natürlich der empfangenen Bytes entspricht.

Dann danke für deine Mühen und die Zeit, die du hierfür investiert hast, trotz des fehlenden Wissens über das AIO.


----------



## Xeonkryptos (19. Apr 2012)

Okay, ich muss zugeben, wenn ich es mit den get-Methoden auslese, dass die clear-Methode benötigt wird. =)

Und nochmal für alle, die es interessiert gibt es ein KSKB von einer Server/Client-Verbindung über das AIO-System.

Server

```
import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.AsynchronousServerSocketChannel;
import java.nio.channels.AsynchronousSocketChannel;
import java.util.ArrayList;
import java.util.List;

public class Server implements Runnable {

	private final int port = 4000;
	private List<AsynchronousSocketChannel> socketList;
	private AsynchronousServerSocketChannel socketChannel;

	public Server() throws IOException {
		socketList = new ArrayList<AsynchronousSocketChannel>();
		socketChannel = AsynchronousServerSocketChannel.open().bind(
				new InetSocketAddress(port));
		socketChannel.accept(null, new Connector(socketChannel, this));
	}

	@Override
	public void run() {
		while (true) {
		}
	}

	public void saveSocketChannel(AsynchronousSocketChannel client) {
		socketList.add(client);
	}

	public void send(String msg) {
		synchronized (socketList) {
			for (AsynchronousSocketChannel client : socketList) {
				client.write(ByteBuffer.wrap(msg.getBytes()));
			}
		}
	}
}
```

ServerReader

```
import java.nio.ByteBuffer;
import java.nio.channels.AsynchronousSocketChannel;
import java.nio.channels.CompletionHandler;

public class ServerReader implements CompletionHandler<Integer, Object> {

	private Server server;
	private ByteBuffer readBuffer;
	private byte[] messageBytes;
	private final AsynchronousSocketChannel socketChannel;

	public ServerReader(AsynchronousSocketChannel socketChannel, Server server) {
		setReadBuffer(ByteBuffer.allocate(10 * 1024));
		this.socketChannel = socketChannel;
		this.server = server;
	}

	@Override
	public void completed(Integer result, Object attachment) {
		System.out.println("SERVER: Nachricht erhalten");
		// Hier würde die Bearbeitung der Nachricht stattfinden.
		readBuffer.clear();
		messageBytes = new byte[result];
		readBuffer.get(messageBytes);
		server.send(new String(messageBytes));
		socketChannel.read(readBuffer, null, this);
	}

	@Override
	public void failed(Throwable exc, Object attachment) {
		System.err.println("Fehler beim Lesen.");
	}

	public AsynchronousSocketChannel getSocketChannel() {
		return socketChannel;
	}

	public ByteBuffer getReadBuffer() {
		return readBuffer;
	}

	private void setReadBuffer(ByteBuffer readBuffer) {
		this.readBuffer = readBuffer;
	}
}
```

Client

```
import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.AsynchronousSocketChannel;
import java.nio.channels.CompletionHandler;
import java.util.Scanner;

public class Client implements CompletionHandler<Void, Object>, Runnable {

	private AsynchronousSocketChannel socketChannel;
	private ByteBuffer buffer = ByteBuffer.allocate(1024);
	private final String host = "localhost";
	private final int port = 4000;

	public Client() throws IOException {
		socketChannel = AsynchronousSocketChannel.open();
		socketChannel.connect(new InetSocketAddress(host, port), null, this);
	}

	@Override
	public void completed(Void result, Object attachment) {
		ClientReader reader = new ClientReader(socketChannel);
		socketChannel.read(reader.getReadBuffer(), null, reader);
	}

	@Override
	public void failed(Throwable exc, Object attachment) {
		System.err.println("Verbindungsfehler");
		try {
			socketChannel.close();
		} catch (IOException e) {
			e.printStackTrace();
		}
	}

	public void send(String msg) {
		buffer = ByteBuffer.wrap(msg.getBytes());
		socketChannel.write(buffer);
	}

	@Override
	public void run() {
		while (true) {
			System.out.println("CLIENT: Nachricht schreiben:");
			send(new Scanner(System.in).nextLine());
		}
	}
}
```

ClientReader

```
import java.nio.ByteBuffer;
import java.nio.channels.AsynchronousSocketChannel;
import java.nio.channels.CompletionHandler;

public class ClientReader implements CompletionHandler<Integer, Object> {

	private ByteBuffer buffer;
	protected byte[] messageBytes;
	private AsynchronousSocketChannel socketChannel;

	public ClientReader(AsynchronousSocketChannel socketChannel) {
		buffer = ByteBuffer.allocate(1024);
		this.socketChannel = socketChannel;
	}

	@Override
	public void completed(Integer result, Object attachment) {
		messageBytes = new byte[result];
		buffer.clear();
		buffer.get(messageBytes);
		System.out.println("CLIENT: Nachricht vom Server: "
				+ new String(messageBytes));
		socketChannel.read(buffer, null, this);
	}

	@Override
	public void failed(Throwable exc, Object attachment) {
		System.err.println("Lesefehler");
	}

	public ByteBuffer getReadBuffer() {
		return buffer;
	}

}
```

Connector des Servers

```
import java.nio.channels.AsynchronousServerSocketChannel;
import java.nio.channels.AsynchronousSocketChannel;
import java.nio.channels.CompletionHandler;

public class Connector implements
		CompletionHandler<AsynchronousSocketChannel, Object> {

	private Server server;
	private AsynchronousServerSocketChannel socketChannel;

	public Connector(AsynchronousServerSocketChannel socketChannel,
			Server server) {
		this.server = server;
		this.socketChannel = socketChannel;
	}

	@Override
	public void completed(AsynchronousSocketChannel result, Object attachment) {
		try {
			ServerReader reader = new ServerReader(result, server);
			result.read(reader.getReadBuffer(), null, reader);
			server.saveSocketChannel(result);
		} catch (Exception e) {
			e.printStackTrace();
		}
		socketChannel.accept(null, this);
	}

	@Override
	public void failed(Throwable exc, Object attachment) {
		socketChannel.accept(null, this);
	}

}
```


----------

