자바

자바 NIO TCP 넌블로킹 - 선택된 키셋, 채널 작업 처리

알통몬_ 2017. 4. 24. 09:37
반응형


안녕하세요 알통몬입니다.

공감 및 댓글은 포스팅 하는데 아주아주 큰 힘이 됩니다!!

포스팅 내용이 찾아주신 분들께 도움이 되길 바라며

더 깔끔하고 좋은 포스팅을 만들어 나가겠습니다^^

 


이전 포스팅과 이어집니다.

2017/04/21 - [자바] - 자바 NIO TCP 넌블로킹 채널 - 넌블로킹 방식의 특징, 셀렉터의 생성과 등록


Selector 를 구현하려면 select()를 호출해야 합니다. select()는 관심 키셋에 저장된 SelectionKey 로부터

작업 처리 준비가 됐다는 통보가 올 때까지 대기합니다(블로킹). 

최소한 하나의 SelectionKey 로부터 작업 처리 준비가 됐다는 통보가 오면 리턴을 하고

리턴 값은 통보해온 SelectionKey 의 수입니다.


위 세 종류의 select() 중에 첫 번째 select()가 자주 사용됩니다.

이 메소드는 블로킹되므로 UI 처리 및 이벤트를 처리하는 스레드에서는 호출이 안되고,

별도의 작업 스레드를 생생해서 실행해야 합니다.


select()가 리턴되는 경우 3가지

 - 채널이 작업 처리 준비가 됐다는 통보를 할 경우

 - Selector 의 wakeup() 을 호출할 경우

 - select() 를 호출한 스레드가 인터럽트 될 경우


ServerSocketChannel은 작업 유형이 OP_ACCEPT 밖에 없지만

SocketChannel 은 상황에 따라 읽기 작업과 쓰기 작업을 번갈아 가면서 작업 유형을 변경할 수 있습니다.

채널의 작업 유형이 변경되면 SelectionKey 의 작업 유형을 interestOps() 로 변경해야 

스레드가 채널 작업을 할 수 있습니다.

예)

SelectionKey의 작업 유형을 OP_WRITE로 변경하는 코드

selectionKey.interestOps(SelectionKey.OP_WRITE);

selectionKey.wakeup();



SelectionKey 의 작업 유형이 바뀌면 Selector 의 wakeup() 을 호출해 블로킹되어 있는 select()를

즉시 리턴하고 변경된 작업 유형을 감지하도록 select()를 다시 실행해야 합니다.

select() 가 1이상의 값을 리턴하는 경우  selectedKeys() 로 작업 처리 준비된 SelectionKey들을

Set 컬렉션으로 얻을 수 있습니다.

=> 선택된 키셋

예)

int keyCount = selector.select();

  if(keyCount>0){

   Set<SelctionKey> selectedKeys = selector.selectedKeys();



채널 작업 처리 :

작업 스레드가 해야 할 일은 선택된 키셋에서 SelectionKey 를 하나 씩 꺼내 작업 유형 별로 채널 작업을

처리하는 것입니다.

어떤 작업 유형인지 알아내려면 SelectionKey 의 메소드 중에서 어떤 것이 true를 리턴하는지 조사하는 것입니다.

예제 코드)

작업 스레드가 작업 유형 별로 채널 작업을 처리하는 코드.

Thread thread = new Thread() {

  @Override

  public void run() {

   while(true) {

    try {

     int keyCount = selector.select();

                                // 작업 처리 준비된 키 감지함

     if(keyCount == 0) { continue; }

     Set<SelectionKey> selectedKeys = selector.selectedKeys();

                                //선택된 키셋 얻음

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

     while (iterator.hasNext()) {

         SelectionKey selectionKey = iterator.next();

         if (selectionKey.isAcceptable()) {

         //연결 수락 작업 처리코드

         } else if (selectionKey.isReadable()) {

          //읽기 작업 처리코드

         } else if (selectionKey.isWritable()) {

          //쓰기 작업 처리코드

         }

         iterator.remove();//선택된 키셋에서 처리 완료된 SelectionKey제거

     }

    } catch (Exception e) {

     if(serverSocketChannel.isOpen()) { stopServer(); }

     break;

    }

    }

   }

  };

  thread.start();


작업 스레드가 채널 작업을 처리하기위해서는 채널 객체가 필요합니다.

SelectionKey 의 channel()을 호출하여 얻을 수 있습니다.

예)

작업 유형이 OP_ACCEPT일 경우 연결 수락 작업 처리에서 필요한 ServerSocketChannel 얻는 코드

ServerSocketChannel serverSocketChannel = (ServerSocketChannel) selectionKey.channel();

작업 스레드가 채널 작업을 처리하다 보면 채널 객체 이외에 다른 객체가 필요하게 될 수 도 있습니다.

이런 객체는 SelectionKey 에 첨부해 두고 사용 가능합니다.

예)

[객체 첨부]

Client client = new Client(socketChannel);

SelectionKey selectionKey = socketChannel.register(selector, SelectionKey.OP_READ);

selectionKey.attach(client);


[첨부된 객체 얻기]

if(selectionKey.isReadable()){

     Client client = (Client)selectionKey.attachment();

     ...

}


이상입니다.

 다음 포스팅에서는 NIO TCP 넌블로킹 채널로 채팅 서버 구현에 대해 공부하겠습니다.

반응형