22

I would like to write an asynchronous server using Java 7 and NIO 2.

But how should I use AsynchronousServerSocketChannel?

E.g. if I start with:

final AsynchronousServerSocketChannel server = 
    AsynchronousServerSocketChannel.open().bind(
        new InetSocketAddress(port));

Then when I do server.accept(), the program terminates because that call is asynchronous. And if I put that code in an infinite loop, an AcceptPendingException is thrown.

Any suggestions on how to write a simple asynchronous server using AsynchronousServerSocketChannel?

Here is my full example (similar to the example in the JavaDoc):

import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.channels.AsynchronousServerSocketChannel;
import java.nio.channels.AsynchronousSocketChannel;
import java.nio.channels.CompletionHandler;

public class AsyncServer {

    public static void main(String[] args) {
        int port = 8060;
        try {
            final AsynchronousServerSocketChannel server = 
                    AsynchronousServerSocketChannel.open().bind(
                            new InetSocketAddress(port));

            System.out.println("Server listening on " + port);

            server.accept("Client connection", 
                    new CompletionHandler<AsynchronousSocketChannel, Object>() {
                public void completed(AsynchronousSocketChannel ch, Object att) {
                    System.out.println("Accepted a connection");

                    // accept the next connection
                    server.accept("Client connection", this);

                    // handle this connection
                    //TODO handle(ch);
                }

                public void failed(Throwable exc, Object att) {
                    System.out.println("Failed to accept connection");
                }
            });
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}
Jonas
  • 121,568
  • 97
  • 310
  • 388
  • You can use Netty framework which is specially for client-server application. It also uses java NIO. It is easy and fast development of server. go through http://netty.io/ – ajay_t Sep 13 '13 at 08:49
  • 8
    @Optimus: I know about netty, but that is not relevant to this question. – Jonas Sep 13 '13 at 08:54

4 Answers4

18

You are on the right track, calling accept() from the completed callback in order to accept more connections should work.

A simple (but ugly) way to prevent the thread from terminating is simply to loop until the thread is interrupted.

// yes, sleep() is evil, but sometimes I don't care
while (true) {
    Thread.sleep(1000);
}

A cleaner way is to use AsynchronousChannelGroup. For instance:

AsynchronousChannelGroup group = AsynchronousChannelGroup.withThreadPool(Executors
            .newSingleThreadExecutor());
AsynchronousServerSocketChannel server = AsynchronousServerSocketChannel.open(group).bind(
            new InetSocketAddress(port));

// (insert server.accept() logic here)

// wait until group.shutdown()/shutdownNow(), or the thread is interrupted:
group.awaitTermination(Long.MAX_VALUE, TimeUnit.SECONDS);

You can tune how threads are handled, see the AsynchronousChannelGroup API docs for more information.

Soulman
  • 2,910
  • 24
  • 21
  • 7
    `// yes, sleep() is evil, but sometimes I don't care` You should care. –  Feb 01 '13 at 19:14
  • 7
    Heh. Keep getting downvotes on this, presumably because of the sleep() call, even though I called it _ugly_ and _evil_, and then demonstrate a nicer way. That seems pretty dogmatic. :-) – Soulman Jun 03 '15 at 22:29
4

Using asynchronous accept is useful if you have something else to do in the same thread. In you case, you are not doing something else so I would use

while(true) {
    AsynchronousSocketChannel socket = server.accept().get();
    System.out.println("Accepted " + socket);
    socket.close();
}
Peter Lawrey
  • 525,659
  • 79
  • 751
  • 1,130
  • @gnarly It does, but accept() returns a different Future each time. – Peter Lawrey Feb 01 '13 at 06:47
  • 1
    My point is your example is a blocking server, which defeats the purpose of [`AsynchronousServerSocketChannel`](http://docs.oracle.com/javase/7/docs/api/java/nio/channels/AsynchronousServerSocketChannel.html). –  Feb 01 '13 at 07:56
  • @gnarly Only the accepting thread is blocking. If you have a handler thread for handling connected sockets, this isn't an issue. – Peter Lawrey Feb 01 '13 at 09:09
  • 1
    You shouldn't use multiple threads in an asynchronous server, that's the beauty of it, the OS handles it all internally and it notifies you when IO needs to be done, therefore you should never ever have a thread waiting for it. Use a [`CompletionHandler`](http://docs.oracle.com/javase/7/docs/api/java/nio/channels/CompletionHandler.html) and it automagically listens for connections to accept. –  Feb 01 '13 at 19:13
  • @gnarly Except of course that Java uses an ExecutorService to perform the read/write and return the Futures. It doesn't use the OS to perform asynchronous operations as such (except perhaps for inifini-band). ;) – Peter Lawrey Feb 01 '13 at 19:19
  • That's the difference between [`AsynchronousServerSocketChannel`](http://docs.oracle.com/javase/7/docs/api/java/nio/channels/AsynchronousServerSocketChannel.html) and [`ServerSocketChannel`](http://docs.oracle.com/javase/7/docs/api/java/nio/channels/ServerSocketChannel.html)? –  Feb 01 '13 at 19:24
  • @gnarly There is multiple implementations. The default uses ExecutorService, however I imagine the Infini-band support means it has it's own implementation but I haven't used that. – Peter Lawrey Feb 01 '13 at 19:33
1

Another alternative is to have your main method wait on a signal before returning. Then if you have some kind of external shutdown command, you just notify the signal and the main thread shuts down.

private static final Object shutdownSignal = new Object();

public static void main(String[] args) {

    ...

    synchronized (shutdownSignal) {
        try {
            shutdownSignal.wait();
        }
        catch (InterruptedException e) {
            // handle it!
        }
    }
}
dOxxx
  • 1,540
  • 1
  • 12
  • 28
  • I would not use "Object" as a identifier either a boolean but not exactly the boolean class because it breaks leaving you with weird side effects (read source why) Best solutions: ~ Synchronize on a private final Object specifically designated for the purpose (neater if someone else might extend our class) ~ Replace shutdownSignal with a final AtomicBoolean that you can alter using get and set methods rather than assignments (you'll still need to synchronize for testing the state) Source: https://telliott.io/node/40 ( why not to use boolean on synchronized) – Jasper Lankhorst Jul 27 '18 at 07:54
  • @JasperLankhorst Your comment makes no sense. This is exactly the use case for wait/notify. There are no weird side effects possible as described in your source article, since there is no assignment occurring here. This is only a signalling mechanism. – dOxxx Jul 28 '18 at 12:28
-2

Use count down latch like the following example

    final AsynchronousServerSocketChannel serverChannel = AsynchronousServerSocketChannel.open();
    InetSocketAddress address = new InetSocketAddress(port);
    serverChannel.bind(address);
    final CountDownLatch latch = new CountDownLatch(1);
    serverChannel.accept(null, new CompletionHandler<AsynchronousSocketChannel, Object>() {
@Override
        public void completed(final AsynchronousSocketChannel channel, Object attachment) {
            serverChannel.accept(null, this);
                        }

});
try {
        latch.await();
    } catch (InterruptedException e) {
        e.printStackTrace();
        Thread.currentThread().interrupt();
    }