# NIO Sockets Architektur Problem



## Gast2 (19. Feb 2010)

Hi,

Ich habe in den letzten Tagen ein bisschen mit NIO Sockets rumgespielt und möchte gerne folgendes Client/Server Verhalten implementieren:

Workflow:

1. Client verbindet auf Server
2. Client sended Daten
3. Server parst Daten und berechnet Antwort
4. Server sended Antwort an Client
5. Client bleibt verbunden
6. Client sended erneut Daten, goto 2.

2. 3. und 4. sind zeitlich unbestimmbar, zwischen 1ms und timeout setting

1. bis 6. kann von x clients parallel auf einen Server aufgerufen werden. 

Also 1000 Client senden kontinuierlich Daten and eine ServerInstanz der dann eine Antwort produziert. Aus performance Gründen muss der Channel/Socket offen bleiben damit der Client wenn er wieder Daten hat nicht neu connecten muss. Die Client sind nicht zwingen Java, kann auch Python, C oder was auch immer sein.

Der Server wird so angelegt

```
public AfsSocketServer(String host, int port, String engineId) throws IOException {
		logger.info("Creating Socket on ",host+":",port);
		ServerSocketChannel serverChannel = ServerSocketChannel.open();
		ServerSocket ss = serverChannel.socket();
		ss.bind(new InetSocketAddress(host, port));
		serverChannel.configureBlocking(false);
		selector = Selector.open();
		serverChannel.register(selector, SelectionKey.OP_ACCEPT);
		setDaemon(true);
		serverRunning = true;
		this.port = port;
		this.host = host;
		this.engineId = engineId;
		start();
	}
```

Der folgende Code funktioniert, natürlich mit dem Nachteil das die Schleife während der Ausführung der Methoden (im Beispiel Code vereinfachte Mockups) zwischen Line 18 und 28 blockiert.

```
while (serverRunning) {
			try {
				selector.select();
				Set<SelectionKey> keys = selector.selectedKeys();
				Iterator<SelectionKey> i = keys.iterator();

				while (i.hasNext()) {
					SelectionKey key = i.next();
					i.remove();
					
					if (key.isAcceptable()) {
						ServerSocketChannel serverChannel = (ServerSocketChannel) key.channel( );
						SocketChannel clientChannel = serverChannel.accept();
						clientChannel.configureBlocking(false);
						clientChannel.register(selector, SelectionKey.OP_READ);
						continue;
					}
					if (key.isReadable()) {
						SocketChannel client = (SocketChannel) key.channel();
						client.register(selector, SelectionKey.OP_READ);
						
						// step 2., 3. and 4.
						String message = readFromChannel();
						client.register(selector, SelectionKey.OP_WRITE);
						String response = processingMessage(message);
						
						ByteBuffer out = ByteBuffer.wrap(responseSocketString.getBytes("UTF8"));
						client.write(out);
						client.register(selector, SelectionKey.OP_READ);
						continue;
					}
					if (key.isWritable()) {
						// 
					}
				}
```

Um das Problem zu beheben dachte ich, ich würde einfach einen thread starten der das "Processing" macht, also im einfachsten Fall

```
if (key.isReadable()) {
						SocketChannel client = (SocketChannel) key.channel();
						new ProcessingThread(client, selector).start();
						continue;
					}
```

Ich lasse jetzt extra mal ThreadPools, WorkerThreads etc raus. Der naive ProcessingThread würde also nur

```
public void run(){
						client.register(selector, SelectionKey.OP_READ);
						
						// step 2., 3. and 4.
						String message = readFromChannel();
						client.register(selector, SelectionKey.OP_WRITE);
						String response = processingMessage(message);

						ByteBuffer out = ByteBuffer.wrap(responseSocketString.getBytes("UTF8"));
						client.write(out);
						client.register(selector, SelectionKey.OP_READ);
					}
```

Auch das funktioniert soweit.

Das Problem das ich habe ist das die Schleife das erstemal durchläuft und den client verbinded. Im zweiten Schleifendurchlauf startet brav ein Thread, ließt vom Stream und returned eine Antwort. Während der Thread rechnet läuft die Schleife weiter. Und in jedem Durchlauf wird eine neuer Thread gestartet... 

```
if (key.isReadable()) {
						logger.warn(key);
						SocketChannel client = (SocketChannel) key.channel();
						new ProcessingThread(client, selector).start();
						continue;
					}
```
2010-02-19 11:35:01,926 [Thread-0] WARN  [TestSocketServer] - sun.nio.ch.SelectionKeyImpl@b8f8eb 
2010-02-19 11:35:01,927 [Thread-0] WARN  [TestSocketServer] - sun.nio.ch.SelectionKeyImpl@b8f8eb 
[...]
2010-02-19 11:35:01,933 [Thread-0] WARN  [TestSocketServer] - sun.nio.ch.SelectionKeyImpl@b8f8eb

Allerdings empfängt der Thread nur im ersten Durchlauf Daten. In den weiteren Druchläufen kommt nur null - was ja auch erwartet wird da der Client nur einmal sended.

Warum wird der "wartende" Client jedesmal wieder im selector als aktiv selektiert? Muss ich mir jetzt Keys die aktiv sind merken und die beim Thread sstarten ignorieren? das sieht mir irgendwie krumm aus:

```
if (key.isReadable()) {
						logger.warn(key);
						if(!busyKeys.contains(key)){
						    SocketChannel client = (SocketChannel) key.channel();
						    new ProcessingThread(client, selector).start();
						}
						continue;
					}
```

Oder ist die ganze Architektur Idee falsch? Ich hab in Java NIO und Java Networking rumgelesen, aber so einen richtig guten Vorschlag hab ich da auch nicht gefunden. Irgendwelche Vorschläge/Tips - was mache ich falsch?


----------



## toni99 (19. Feb 2010)

... zwar in englisch, gibt aber ein Grundverständnis Architecture of a Highly Scalable NIO-Based Server | Java.net


----------



## Gast2 (19. Feb 2010)

Englisch ist kein Problem. Die Seite sieht gut aus. Werd ich mal durcharbeiten.

Danke...


----------

