NIO (New IO)


자바에서 Socket 통신을 할 때 기존 IO 방식에서는 네트워크 처리와 속도에 대해 매우 제한적이었다.


하지만 JDK1.3 버전 이후 Java IO 대신 NIO를 사용하여 한계를 보안하였다.





[IO Process]

 



: 블로킹 문제를 해결하기 위해 클라이언트마다 쓰레드가 추가 되었고 멀티쓰레드 환경에서 수많은 쓰레드로 인해 메모리의 오버헤드가 발생할 수 있다.


  






[NIO Process]



: Selector는 멀티쓰레드 환경에서 여러 클라이언트의 작업요청에 대응할 수 있는 좋은 방법이다. 

 작업을 수행하는 work 쓰레드는 1개 이상이 될 수 있다.   

 물론 클라이언트가 너무 많으면, 이 과정에서 selector와 work 쓰레드가 문제가 발생할 수 있다.

 Accept와 Read 단계에서 SelectorPoolThreadPool을 사용하여 성능을 고려할 수 있다.






Selector Server 간단한 예제



    

package selector; import java.io.IOException; import java.net.InetSocketAddress; import java.net.Socket; import java.net.SocketAddress; import java.nio.ByteBuffer; import java.nio.channels.SelectionKey; import java.nio.channels.Selector; import java.nio.channels.ServerSocketChannel; import java.nio.channels.SocketChannel; import java.util.Arrays; import java.util.Iterator; public class Server implements Runnable { private Selector select; private InetSocketAddress sockAddr; public Server(String host, int port) { sockAddr = new InetSocketAddress(host, port); } public void accept(SelectionKey key) { ServerSocketChannel serverChannel = (ServerSocketChannel) key.channel(); SocketChannel channel; try { channel = serverChannel.accept(); channel.configureBlocking(false); Socket socket = channel.socket(); SocketAddress remoteAddr = socket.getRemoteSocketAddress(); System.out.println("Connected to: " + remoteAddr); channel.register(key.selector(), SelectionKey.OP_READ); // 읽을 수 있는 모드로 전환 } catch (Exception e) { // TODO: handle exception } } public void read(SelectionKey key) { SocketChannel channel = (SocketChannel) key.channel(); ByteBuffer buffer = ByteBuffer.allocate(1024); int numRead = -1; try { numRead = channel.read(buffer); if (numRead == -1) { // 아직 읽지 않았다면 읽는다. channel.close(); key.cancel(); return; } else { System.out.println("read:" + new String(buffer.array())); channel.close(); key.cancel(); return; //읽고 종료 } } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } } @Override public void run() { // TODO Auto-generated method stub try { // selector 열기 select = Selector.open(); // 서버 소켓 채널 열기 ServerSocketChannel sockChannel = ServerSocketChannel.open(); sockChannel.configureBlocking(false); // blocking 모드 false sockChannel.bind(sockAddr); System.out.println("Start Server:"+sockChannel.getLocalAddress()); // 서버 셀렉터를 클라이언트 연결을 수용하기 위한 키로 등록합니다. sockChannel.register(select, SelectionKey.OP_ACCEPT); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } while (true) { try { select.select(); // 여기서 Blocking 됨. 등록된 키를 가져온다. // select.selectNow(); // 비동기 처리. Iterator<?> keys = select.selectedKeys().iterator(); while (keys.hasNext()) { SelectionKey key = (SelectionKey) keys.next(); if (!key.isValid()) { // 사용가능한 상태가 아니면 그냥 넘어감. continue; } if (key.isAcceptable()) { // select가 accept 모드이면 accept(key); } else if (key.isReadable()) { // select가 read 모드이면 read(key); } keys.remove(); // 중요함 . 처리한 키는 제거 } } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } } } public static void main(String[] args) { // TODO Auto-generated method stub Server server = new Server("localhost", 8080); server.run(); } }





+ Recent posts