Как использовать AsynchronousServerSocketChannel для приема соединений?
Я хотел бы написать асинхронный сервер с использованием Java 7 и NIO 2.
Но как мне использовать AsynchronousServerSocketChannel
?
например. если я начну с:
final AsynchronousServerSocketChannel server =
AsynchronousServerSocketChannel.open().bind(
new InetSocketAddress(port));
Тогда, когда я делаю server.accept()
, программа завершает, потому что этот вызов является асинхронным. И если я помещаю этот код в бесконечный цикл, бросается AcceptPendingException
.
Любые предложения по написанию простого асинхронного сервера с помощью AsynchronousServerSocketChannel
?
Вот мой полный пример (похожий на пример в 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();
}
}
}
Ответы
Ответ 1
Вы находитесь на правильном пути, вызывая accept() из завершенного обратного вызова, чтобы принимать больше соединений, должны работать.
Простой (но уродливый) способ предотвратить завершение потока - это просто цикл, пока поток не будет прерван.
// yes, sleep() is evil, but sometimes I don't care
while (true) {
Thread.sleep(1000);
}
Более чистый способ - использовать AsynchronousChannelGroup
. Например:
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);
Вы можете настроить, как обрабатываются потоки, см. Документы API асинхронной связи для получения дополнительной информации.
Ответ 2
Использование асинхронного принятия полезно, если у вас есть что-то еще в одном потоке. В вашем случае вы ничего не делаете, поэтому я бы использовал
while(true) {
AsynchronousSocketChannel socket = server.accept().get();
System.out.println("Accepted " + socket);
socket.close();
}
Ответ 3
Другая альтернатива заключается в том, чтобы ваш основной метод дождался сигнала перед возвратом. Затем, если у вас есть какая-то внешняя команда отключения, вы просто уведомляете сигнал, и основной поток отключается.
private static final Object shutdownSignal = new Object();
public static void main(String[] args) {
...
synchronized (shutdownSignal) {
try {
shutdownSignal.wait();
}
catch (InterruptedException e) {
// handle it!
}
}
}
Ответ 4
Используйте защелку с обратным отсчетом, как в следующем примере
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();
}