본문 바로가기

Java

[NIO] Selection

네트워크 프로그래밍을 위한 새 I/O API 의 두 번째 부분은 준비된 것을 선택하는 것이다.

준비된 채널을 선택함으로써 읽고 쓸때 블록하지 않아도 된다.

이 내용은 주로 서버의 관심사이지만, 여러 개의 창을 띄워서 동시에 여러 연결을 시도하는 웹 스파이더나 브라우저 같은 클라이언트 프로그램에서도 활용할 수 있다.

준비된 것을 선택하기 위해서는 서로 다른 채널들이 Selector 객체에 등록되어야 하며, 이때 각 채널에 할당되는 SelectionKey 가 사용된다. 그리고 나서 프로그램은 Selector 객체에게 작업을 수행할 준비가 된 채널 키의 세트를 요청한다.

Selector class

셀렉터에 있는 유일한 생성자는 protected 로 선언되어 있다. 일반적으로 새로운 셀렉터는 정적 팩토리 메소드인 Selector.open() 을 호출하여 생성된다.

public static Selector open() throws IOException

생성한 이후의 다음 단계는 채널을 셀렉터에 추가하는 것이다. Selector 클래스에는 채널을 추가하는 메소드가 없다.

반대로 register() 메소드가 SelectableChannel 클래스에 선언되어 있다. 모든 채널이 선택 가능한 것은 아니지만 모든 네트워크 채널은 선택 가능하다. (FileChannels 는 선택할 수 없다.)

public abstract SelectionKey register(Selector sel, int ops, Object att)
        throws ClosedChannelException;

public final SelectionKey register(Selector sel, int ops)
    throws ClosedChannelException
{
    return register(sel, ops, null);
}

위의 메소드에서 두 번째 인자는 채널에 대해 등록할 연산을 나타내는 비트 상수이다.

이들은 비트 플래그 정수 상수 이므로 하나의 셀렉터에 여러 동작을 등록해야 할 경우, 비트 연산자를 사용하여 상수 값을 합칠 수 있다.

channel.register(selector, SelectionKey.OP_ACCEPT | SelectionKey.OP_WRITE);

서로 다른 채널을 셀렉터에 등록한 이후, 어느 채널이 처리할 준비가 됐는지 셀렉터에게 언제든지 물을 수 있다. 채널들은 몇몇 연산은 준비가 되어 있지만 준비되지 않은 연산도 있을 수 있다.

(예를 들어, 한 채널이 읽을 준비는 됐는데 쓸 준비는 안 됐을 수 있다.)

준비된 채널을 선택하는데는 세 가지 메소드가 제공된다.

public abstract int selectNow() throws IOException // non - block
public abstract int select() throws IOException // block
public abstract int select(long timeout) throws IOException // block

준비된 채널이 있는 경우, selectedKeys() 메소드를 호출하여 준비된 채널을 구할 수 있는데, 주의해야할 점은 처리가 끝난 키는 반드시 iterator 에서 제거해야 한다는 점이다. 그렇지 않으면 셀렉터는 아직 작업이 끝나지 않았다고 판단하고 루프 회전시마다 계속해서 반환한다.

SekectionKey class

SelectionKey 객체는 채널에 포인터로 제공된다. SelectionKey 는 또한 객체를 첨부하고 있으며, 보통 해당 채널의 연결 상태를 보관하고 있다.

먼저 해당 키에 준비된 작업을 확인해야 한다. 해당 키에 준비된 작업은 다음 네 가지 중 하나이다.

public final boolean isAcceptable()
public final boolean isConnectable()
public final boolean isReadable()
public final boolean isWritable()

키와 관련된 채널의 준비된 작업이 확인되고 나면, channel() 메소드를 호출하여 채널을 구한다.

public abstract SelectableChannel channel()

상태 정보를 유지하기 위해 SelectionKey 에 객체를 저장해 뒀다면, attachmenet() 메소드를 호출하여 이 객체를 얻을 수 있다.

public final Object attachment()

해당 연결의 작업이 끝날 때 연결의 SelectionKey 객체를 셀렉터에서 제거하여 셀렉터가 리소스를 낭비하지 않도록 한다. cancel() 메소드를 호출하여 이 작업을 수행한다.

public abstract void cancel()

하지만 위의 동작은 채널을 닫지 않은 경우에만 필요하다. 채널을 닫게 되면 자동으로 모든 셀렉터로부터 해당 채널의 모든 키를 해제한다. 마찬가지로 셀렉터를 닫게되면 등록된 모든 키들이 더 이상 효력이 없어진다.

  • 참고 : 자바 네트워크 프로그래밍

'Java' 카테고리의 다른 글

[NIO] FileChannel 과 FileInput/Output Stream 과의 차이  (0) 2024.04.21
[NIO] Socket Channel  (0) 2021.05.08