2012-01-20 39 views
18

Me gustaría escribir un servidor asíncrono con Java 7 y NIO 2.¿Cómo debo usar AsynchronousServerSocketChannel para aceptar conexiones?

Pero, ¿cómo debo usar AsynchronousServerSocketChannel?

E.g. si comienzo con:

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

Entonces cuando hago server.accept(), el programa termina porque esa llamada es asíncrona. Y si pongo ese código en un bucle infinito, se arroja un AcceptPendingException.

¿Alguna sugerencia sobre cómo escribir un servidor asincrónico simple usando AsynchronousServerSocketChannel?

Aquí está mi ejemplo completo (similar al ejemplo en el 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(); 
     } 
    } 
} 
+0

Puede utilizar marco Netty que es especialmente para aplicaciones cliente-servidor. También usa java NIO. Es un desarrollo fácil y rápido del servidor. ir a través de http://netty.io/ –

+3

@Optimus: sé de netty, pero eso no es relevante para esta pregunta. – Jonas

Respuesta

9

Está en el camino correcto, llamando a aceptar() de la devolución de llamada completada con el fin de aceptar más conexiones deben trabajar.

Una forma simple (pero fea) de evitar que el hilo termine es simplemente hacer un bucle hasta que se interrumpa el hilo.

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

Una manera más limpia es usar AsynchronousChannelGroup. Por ejemplo:

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); 

Puede sintonizar cómo se manejan los hilos, consulte la AsynchronousChannelGroup API docs para más información.

+4

'// sí, dormir() es malo, pero a veces no me importa' Deberías importarte. –

+4

Heh. Siga obteniendo votos atrasados ​​sobre esto, presumiblemente debido a la llamada de suspensión (sleep), aunque lo llamé _ugly_ y _evil_, y luego demuestro una forma más agradable. Eso parece bastante dogmático. :-) – Soulman

3

Uso asíncrono aceptar es útil si tiene algo más que hacer en el mismo hilo. En caso de que usted, usted no está haciendo otra cosa por lo que me gustaría utilizar

while(true) { 
    AsynchronousSocketChannel socket = server.accept().get(); 
    System.out.println("Accepted " + socket); 
    socket.close(); 
} 
+0

Future.get() espera ... –

+0

@gnarly Sí, pero accept() devuelve un Future diferente cada vez. –

+0

Mi punto es que su ejemplo es un servidor de bloqueo, que derrota el propósito de ['AsynchronousServerSocketChannel'] (http://docs.oracle.com/javase/7/docs/api/java/nio/channels/AsynchronousServerSocketChannel.html) . –

2

Otra alternativa es que su método principal espere una señal antes de volver. Entonces, si tiene algún tipo de comando de apagado externo, simplemente notifica la señal y el hilo principal se apaga.

private static final Object shutdownSignal = new Object(); 

public static void main(String[] args) { 

    ... 

    synchronized (shutdownSignal) { 
     try { 
      shutdownSignal.wait(); 
     } 
     catch (InterruptedException e) { 
      // handle it! 
     } 
    } 
} 
0

Uso cuenta atrás pestillo como el ejemplo siguiente

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(); 
    } 
Cuestiones relacionadas