java.nio.channels.Selector 사용법 정리


한개의 Process에서 여러개의 Socket을 다루는 방법은 크게 두가지가 있다. 


  1. Thread 

  2. IO Multiplexing


내가 10년 전에 C/C++로 네트웍 프로그래밍을 땐 간단했었다. select() (참고 : http://man7.org/linux/man-pages/man2/select_tut.2.html ) 함수 하나만 제대로 알면 모든 것을 정복할 수 있었다. 게다가 Blocking Socket 으로 Multiplexing을 구현할 수도 있었다. C/C++에선 간결한 API를 통해 너무 쉽게 적용할 수 있었다. 


최근 Java로 네트웍 프로그래밍 할 일이 있었는데, 처음 접한 네트웍 프로그래밍이라 그런지 복잡하게 느껴졌다. C의 select() 를 대신할 Selector부터 쉬운 접근은 아니었다


다음을 차근 차근 따라해보면 답이 보이겠지만 처음이라면 쉬운 접근은 아닌 듯 하다. 


  1. selector = Selector.open()

  2. server = ServerSocketChannel.open(); // Server Socket 을 여는 경우

      server.socket().bind(new InetSocketAddress( PORT ));

       server.configureBlocking(false);

       server.register(selector, SelectionKey.OP_ACCEPT);

      

      SocketChannel ( ServerSocketChannel )객체에서 register(selector, Options) 해야 함. 

      4가지 옵션이 있음. SelectionKey.OP_ACCEPT, OP_READ, OP_WRITE, OP_CONNECT

      단독으로 쓰거나 중복 가능. 중복은 ( SelectionKey.OP_READ | SelectionKey.OP_WRITE )

      register() 전에 configureBlocking(false) 해서 non-blocking 모드로 설정해야 함.


  3. selector.select() 를 통해 옵션에 따른 대기 중인 SocketChannel을 알아내기.


  4. 각 상황에 맞는 처리.

       Iterator<SelectionKey> iter = selector.selectedKeys().iterator();

       while(iter.hasNext())

       {

             SelectionKey key = iter.next();

             if( key.isAcceptable() )

             {

ServerSocketChannel serversocket = (ServerSocketChannel) key.channel();

SocketChannel client = serversocket.accept();

             }

             else if( key.isReadAble() )

             {

                 SocketChannel socket = (SocketChannel) key.channel();

             }

             else if( key.isWriteAble() )

             {

                 SocketChannel socket = (SocketChannel) key.channel();

             }

       }


  0. 끊거나 끊겼을 경우

key.cancel();

key.channel.close();

      보통 Socket이 끊겼는지 여부는 read()을 호출하였을 때 return value가 -1이면 끊긴 것이다.



내가 평소에 사용하는 방법


Selector selector = null;

ServerSocketChannel server = null;

selector = Selector.open();

server = ServerSocketChannel.open();


server.socket().bind(new InetSocketAddress( PORT ));

server.configureBlocking(false);

server.register(selector, SelectionKey.OP_ACCEPT);


while(true)

{

int rv = selector.select(300);

if (rv == 0)

continue

Iterator<SelectionKey> iter = selector.selectedKeys().iterator();

while(iter.hasNext())

{

  SelectionKey key = iter.next();

if ( key.isAcceptable() )

{


SocketChannel client = key.channel().accept();


if (client != null)

{

client.configureBlocking(false);

client.register(selector, SelectionKey.OP_READ); // 수신만 원하는 경우

}

}

if ( key.isReadable() )

{

   SocketChannel socket = (SocketChannel) key.channel();

}

else if( key.isWriteable() )

{

               SocketChannel socket = (SocketChannel) key.channel();

}

}

}


Posted by Picky Kang

댓글을 달아 주세요