4

I have to implemented a server which should accept more connections. Without any deeper thoughts I decided to use the new JAVA NIO.2 classes.

My current approach is:

final Semaphore wait = new Semaphore(1);
while(true){
        wait.acquire();
        this.asyncSocket.accept(null, new CompletionHandler<AsynchronousSocketChannel, Void>() {
            @Override
            public void completed(AsynchronousSocketChannel result, Void attachment) {
                wait.release();
                asyncSocket.accept(null, this);
                ...
             }
             ...
        }
}

if I don't add the semaphore, I get an AcceptPendingException. It works, however, I don't know if that's the proper way to implement a server which can handle more open sockets.

Another approach was:

final Semaphore wait = new Semaphore(1);
    while(true){
        wait.acquire();
        final Future<AsynchronousSocketChannel> futureChannel = this.asyncSocket.accept();

        this.exec.execute(new Runnable() {
            @Override
            public void run() {
                try (final AsynchronousSocketChannel clientChannel = futureChannel.get()) {
                    wait.release();
                    try (ObjectInputStream ois = new ObjectInputStream(Channels.newInputStream(clientChannel))) {
                        final Command cmd = (Command) ois.readObject();

                        cmd.execute(util, clientChannel, null, logger).run();
                    }
                } catch (IOException | InterruptedException | ClassNotFoundException | ExecutionException e) {
                    e.printStackTrace();
                }
            }
        });

Why I'm unhappy with both solutions? Unfortunately, in both implementations, the server leaves a lot of open sockets in state TIME_WAIT, although I'm closing the it on the server as well on the client side..

So actually I have 2 questions:

  • What's a proper way to use AsynchronousServerSocketChannel to implement a Server which accepts more connections.
  • How to get rid of the open sockets in state TIME_WAIT

EDIT:

private <T extends Serializable> T sendCommand(final Command<T> command) throws ExecutionException, InterruptedException, IOException, ClassNotFoundException {
    T result = null;

    try (final AsynchronousSocketChannel channel = AsynchronousSocketChannel.open(channelGroup)) {
        channel.setOption(StandardSocketOptions.SO_REUSEADDR, true);
        channel.connect(this.mwInfo.getNextMiddleware()).get();

        final OutputStream os = Channels.newOutputStream(channel);
        final InputStream is = Channels.newInputStream(channel);

        try (final ObjectOutputStream oos = new ObjectOutputStream(os)) {
            oos.writeObject(command);
            oos.flush();

            try (final ObjectInputStream ois = new ObjectInputStream(is)) {
                result = (T) ois.readObject();
            }
        }
    }

    return result;
}

Thanks in advance!

3
  • 1
    you essentially have one thread blocking on accept. That is ok though, since you have only one server socket. Commented Oct 9, 2015 at 17:45
  • TIME_WAIT is normal; whichever end that initiates FIN will be in TIME_WAIT state. It is recommended that the server is in that state. RFC1122 allows the connection to be reopen if it's in TIME_WAIT state on the server side. Commented Oct 9, 2015 at 17:51
  • TIME_WAIT is a precaution, to make sure all segments of the connection have died out, before the connection (identified by ip/ports) can be reopen. The default wait period is very long though (probably from old days); you can set it shorter in your OS. Commented Oct 9, 2015 at 18:01

1 Answer 1

1

I can only answer the second question (no knowledge of Java socket specifics). To get rid of those sockets you must implement 'graceful shutdown' protocol on socket. That is, one socket does shutdown on send, another one does shutdown on send upon seing that, than sockets to shutdown on recv. This will ensure no sockets will stay in TIME_WAIT state.

The other option would be to fiddle with SO_LINGER option of the socket, but this is ill-advised.

I also notice, that people seems to just use SO_REUSEADDR as a universal solution. You can't bind to a port? Specify SO_REUSEADDR and all your problems will go away... and this is wrong! If SO_REUSEADDR is universal solution, why is it not ON by default? Because it is dangerous. When you specify SO_REUSEADDR, you create another socket on the same port and now you can start seeing messages from the previous connection! This is not very likely to happen, of course. But it can happen! Imagine what kind of bug would it be to troubleshoot!

Sign up to request clarification or add additional context in comments.

7 Comments

Thanks for your answer! On the client side it works perfectly with ` channel.setOption(StandardSocketOptions.SO_REUSEADDR, true);` Thus, i have no sockets in TIME_WAIT state. But, to be honest, i don't know what you mean. So, the client sends something, and it has to wait until it receives the response. But who closes the connection first? Thanks in advance!
i added the connection-method for the client.
Hoi Laszlo, thanks! But, if only the client closes the socket, then the socket stays in TIME_WAIT (and do not a "graceful shutdown")?
Thanks for your clarification with SO_REUSEADDR! Very important point! But, how would you implement the server, to as you said "graceful shutdown" the connection?
This answer is simply incorrect. There will always be a TIME_WAIT state on one side or the other, specifically the side that sends the first FIN.
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.