0

Regarding Java NIO2.

Suppose we have the following to listen to client requests...

asyncServerSocketChannel.accept(null, new CompletionHandler <AsynchronousSocketChannel, Object>() {
    @Override
    public void completed(final AsynchronousSocketChannel asyncSocketChannel, Object attachment) {
         // Put the execution of the Completeion handler on another thread so that 
         // we don't block another channel being accepted.
         executer.submit(new Runnable() {
             public void run() {
                 handle(asyncSocketChannel);
             }
         });

         // call another.
         asyncServerSocketChannel.accept(null, this);
     }

     @Override
     public void failed(Throwable exc, Object attachment) {
         // TODO Auto-generated method stub
     }
});

This code will accept a client connection process it and then accept another. To communicate with the server the client opens up an AsyncSocketChannel and fires the message. The Completion handler completed() method is then invoked.

However, this means if the client wants to send another message on the same AsyncSocket instance it can't.

It has to create another AsycnSocket instance - which I believe means another TCP connection - which is performance hit.

Any ideas how to get around this?

Or to put the question another way, any ideas how to make the same asyncSocketChannel receive multipe CompleteionHandler completed() events?

edit: My handling code is like this...

public void handle(AsynchronousSocketChannel asyncSocketChannel) {
    ByteBuffer readBuffer = ByteBuffer.allocate(100);
    try {
        // read a message from the client, timeout after 10 seconds 
        Future<Integer> futureReadResult = asyncSocketChannel.read(readBuffer);
        futureReadResult.get(10, TimeUnit.SECONDS);
        String receivedMessage = new String(readBuffer.array());

        // some logic based on the message here...               

        // after the logic is a return message to client
        ByteBuffer returnMessage = ByteBuffer.wrap((RESPONSE_FINISHED_REQUEST + " " + client
                 + ", " + RESPONSE_COUNTER_EQUALS + value).getBytes());
        Future<Integer> futureWriteResult = asyncSocketChannel.write(returnMessage);
        futureWriteResult.get(10, TimeUnit.SECONDS);
   } ...

So that's it my server reads a message from the async channe and returns an answer. The client blocks until it gets the answer. But this is ok. I don't care if client blocks.

Whent this is finished, client tries to send another message on same async channel and it doesn't work.

dublintech
  • 16,815
  • 29
  • 84
  • 115

2 Answers2

3

There are 2 phases of connection and 2 different kind of completion handlers. First phase is to handle a connection request, this is what you have programmed (BTW as Jonas said, no need to use another executor). Second phase (which can be repeated multiple times) is to issue an I/O request and to handle request completion. For this, you have to supply a memory buffer holding data to read or write, and you did not show any code for this. When you do the second phase, you'll see that there is no such problem as you wrote: "if the client wants to send another message on the same AsyncSocket instance it can't".

One problem with NIO2 is that on one hand, programmer have to avoid multiple async operations of the same kind (accept, read, or write) on the same channel (or else an error occur), and on the other hand, programmer have to avoid blocking wait in handlers. This problem is solved in df4j-nio2 subproject of the df4j actor framework, where both AsyncServerSocketChannel and AsyncSocketChannel are represented as actors. (df4j is developed by me.)

Alexei Kaigorodov
  • 13,189
  • 1
  • 21
  • 38
  • @Alexei Kaigorodv I have added some more code. Does my problem make sense now? – dublintech Oct 22 '12 at 20:37
  • 1
    I see your point, but you can invoke `handle(AsynchronousSocketChannel)` multiple times: first after the connection acceptance, and then after the next message have been read. Or the `handle` method can have a loop inside. And the way you do I/O operations (with `Future`s), yes you cannot do it in the connection completion handler, but have to do it on a separate thread. You can use a thread pool for this, but this has to be an unbounded one, or a thread starvation deadlock can occur. – Alexei Kaigorodov Oct 22 '12 at 20:46
  • Sorry. I am a bit confused. Are you saying I should not use futures? – dublintech Oct 22 '12 at 22:46
  • 1
    @dublintech - if you want to build a truly asynchronous program - yes. In callbacks (e.g. `CompletionHandler`s) you cannot use blocking operations, including `Future.get()`. Imagine there are 2 worlds - world of threadpools which run callbacks and world of threads. Futures connect both worlds, but not callbacks. – Alexei Kaigorodov Oct 23 '12 at 03:50
  • @Alexis Kaigorodov So, I should rewrite handle using callbacks. My question is thou if my Server is spawning threads, why is this any better than the classical approach of using a ServerSocket which handles requests on spawned threads? – dublintech Oct 23 '12 at 08:31
  • 1
    @dublintech It's not any better. But if you rewrite handle using callbacks, it would be better because of using little number of threads (in threadpool) for large number of connections. And if your number of connections is not that large (<1000), using nio2 become questionable. – Alexei Kaigorodov Oct 23 '12 at 08:55
  • @Alexis Kaigorodov Ok, so the model using nio2 / callbacks is better than classic thread per client because you can serve large number of connections with less threads. But what if you I just use a thread pool in the classic thread per client approach? Sorry just trying to tease what the actual advantage of nio2 /callbacks is. And I am still a bit confused. – dublintech Oct 23 '12 at 09:01
  • 1
    if you use a thread pool in the classic thread per client approach, then if the threadpool has thread number limit, then some client requests has to wait for a free thread to serve them, and if the thread number is not limited (cached threapool), then this is practically the same as creating threads for each new client. – Alexei Kaigorodov Oct 23 '12 at 09:26
  • But why is nio2 / callbacks using threadpool any better than thread per client approach using thread pool? Where is the performance benefit? – dublintech Oct 23 '12 at 09:49
  • 1
    because callbacks do not block threads. In thread per client, thread sends I/O requests and waits, and almost all the time the thread is idle, doing nothing and wasting memory. Callback sends request and exits, freeing thread for another callback. When the I/O request is completed, again a thread is used for a little amount of time. – Alexei Kaigorodov Oct 23 '12 at 12:06
  • @Alexei Kaigorodov Many thanks. Yes that explains it. In the thread per client model (with thread pool), when the I/O is happening, a Java thread is running but not really doing anything. But in nio2 / callback model, the I/O is not happening on an application thread. The application thread is notified when the I/O has finished and the callback then does some logic. – dublintech Oct 23 '12 at 14:33
1

First, you should not use an executer like you have in the completed-method. The completed-method is already handled in a new worker-thread.

In your completed-method for .accept(...), you should call asychSocketChannel.read(...) to read the data. The client can just send another message on the same socket. This message will be handled with a new call to the completed-method, perhaps by another worker-thread on your server.

Jonas
  • 121,568
  • 97
  • 310
  • 388
  • There is a tip from JavaOne to put the completion handler completed() on a separate thread. See here: page 34. http://openjdk.java.net/projects/nio/presentations/TS-4222.pdf and completed)( will not get called again. Hence this question. – dublintech Oct 22 '12 at 19:33
  • @dublintech: Yes, that is for **long lived operations** (e.g. parsing a huge XML file) which I guess you are not working with here. – Jonas Oct 22 '12 at 19:39
  • I perfom some logic and then I write an answer back to the client. That's it. – dublintech Oct 22 '12 at 20:37