问题
I have to implement 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 thats 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've 2 questions:
- Whats a proper way to use
AsynchronousServerSocketChannel
to implement a Server wich 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!
回答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!
来源:https://stackoverflow.com/questions/33044059/multithreaded-server-with-asynchronousserversocketchannel