NIO가 무엇일까요?
NonBloking Input-Output 입니다. 기존의 일반 IO는 항상 블럭화가 되면서, 처리를 해왓습니다.
블럭화가되면 그블럭이 풀리기전까진 아무것도 수행할수 없지요. 그래서 그것이 큰 단점이기도 하지요.
NIO는 JDK 1.4 이전에는
존재 하지 않는 방법론입니다. 그 이후에 생긴 방법론이므로 참고 하시면 되구요.
어쨋든 그래서 NIO가 무엇이냐면.. 블럭화(잠수)를
타지않고 작업을 할수있게 해주는 방법론이에요.
서버가 존재하고 통신을 할때 메인스레드가 블럭화(잠수)를 타버리면 그 블럭화가 풀리기전까진
아무것도 못하잖아요..?
이것은 블럭을 시키지않고서, 누가 들어오던지 자료를 요청하던지 블럭 시키지않고 그냥 무작정 일만
처리시키는것이죠.
하나 이상적인 예를 들어볼가요?
사장 : 어이 신입.. 내일까지 3천장 A4용지 복사좀 해놓게나... 신입 : 예!! 1시간후... 부장 : 어이 신입.. 미안한데말이야... 프로그래밍 유지보수가 생겨버렷어... 저 코드좀 내일까지 고쳐주게나... 신입 : 네 -_-; 과장 : 이보게나.. 가족사진이 있는데 그지가치 나왓어 좀 고쳐준나.. 신입 : 네 ㅜㅜ 위에서 신입이라는 사람이 있지요.. 지금 3가지 일을 부여 받았어요. 지금 신입이 해야되는일은 (전화받기, 복사하기, 코딩하기) 입니다. 이제 위의 상황을 IO와 NIO로 어떻게 처리할수있는지 비교해보겠씁니다. 1. I/O 를 이용한 일처리 - 사장이 시킨 3천장 복사를 다한다. 그거다하기전까진 다 쌩간다. - 그담에 부장이 시킨 코딩을 한다... - 마지막 과장이 시킨 사진 작업을한다. 2. N/I/O를 이용한 일처리 - 복사기를 돌려놓고, 코딩두 하면서, 포토샵을 켜서 사진작업을합니다 무조건 다할대까지 하는것이 아니라.. 계속 요청 들어오는데로 돌아가면서 하는것입니다. |
채널은 스트림과같이 연결된 것들 끼리 읽기 쓰기 동시에 이루어질수록 하게 해주는것입니다.
일반IO는 입력용, 출력용 따로있었지요~
그리고 Selector는 c에서의 select와 비슷한거지만 모든 연결을 중앙집중식으로 관리해주는것 입니다.
즉, 이 Selector만 스레드로 생겨나고 블로킹되어 모든 클라이언트의 연결을 책임지고 관리해 줍니다.
만약, 클라가 서버와 자료를 전송하기 위해 특정 포트와 통신한다면 이 Selector가 이를 감시하여
이벤트 발생한 IO와 관련된 채널을 불러와서 작업을 수행하는 방법입니다.
누군가 요청을 하게되면 CONNECT인지 READ인지 WRITE인지 구분을 할수있습니다.
결론 아무리 많은 클라이언트가 접속을 해도 Selector 를 담당하는부분만 쓰레드 한개로 처리시키면 백점입니다. ㅋ
이정도면 개념을 어느정도 잡을수 있을겁니다. 이보다 개념적으로 더 자세히 알고싶으시다면...
이곳을 참고하세요. -> http://www.javacafe.or.kr/lecture/cafeLecture/network/nio/microsoft1_SJH.html
이제 간단한 채팅에코 서버를 구현해볼까요..?
NIO 서버
package com.nio; import java.io.IOException; import java.net.InetSocketAddress; import java.net.ServerSocket; import java.nio.ByteBuffer; import java.nio.channels.ClosedChannelException; import java.nio.channels.SelectionKey; import java.nio.channels.Selector; import java.nio.channels.ServerSocketChannel; import java.nio.channels.SocketChannel; import java.util.Iterator; import java.util.Vector; public class NioServer { private Selector selector = null; private ServerSocketChannel serverSocketChannel = null; private ServerSocket serverSocket = null; private Vector room = new Vector(); public void initServer() { try { selector = Selector.open(); // Create ServerSocket Channel serverSocketChannel = ServerSocketChannel.open(); // 비 블록킹 모드로 설정한다. serverSocketChannel.configureBlocking(false); // 서버소켓 채널과 연결된 서버소켓을 가져��다. serverSocket = serverSocketChannel.socket(); // 주어진 파라미터에 해당��는 주소다. 포트로 서버소켓을 바인드한다. InetSocketAddress isa = new InetSocketAddress("localhost", 9999); serverSocket.bind(isa); // 서버소켓 채널을 셀렉터에 등록한다. serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT); } catch (IOException ex) { ex.printStackTrace(); } } public void startServer() { System.out.println("Server is Started...."); try { while (true) { selector.select(); // 셀렉터�� select() 메소드로 준비된 이벤트가 있는지 확인한다. Iterator it = selector.selectedKeys().iterator(); while (it.hasNext()) { SelectionKey key = (SelectionKey) it.next(); if (key.isAcceptable()) { accept(key); } else if (key.isReadable()) { read(key); } it.remove(); } // end of while(iterator) } } catch (Exception ex) { ex.printStackTrace(); } } private void accept(SelectionKey key) { ServerSocketChannel server = (ServerSocketChannel) key.channel(); try { // 서버소켓 채널�� accept() 메소드로 서버소켓을 생성한다. SocketChannel sc = server.accept(); // 생성된 소켓채널을 비 블록킹과 읽기 모드로 셀렉터에 등록한다. if (sc == null) return; sc.configureBlocking(false); sc.register(selector, SelectionKey.OP_READ); room.add(sc); // 접속자 추가 System.out.println(sc.toString() + "클라이언트가 접속했습니다."); } catch (Exception ex) { ex.printStackTrace(); } } private void read(SelectionKey key) { // SelectionKey로부터 소켓채널을 얻어��다. SocketChannel sc = (SocketChannel) key.channel(); // ByteBuffer를 생성한다. ByteBuffer buffer = ByteBuffer.allocateDirect(1024); try { // 요청한 클라이언트�� 소켓채널로부터 데이터를 읽어들인다. int read = sc.read(buffer); } catch (IOException ex) { try { sc.close(); } catch (IOException e) { } room.remove(sc); ex.printStackTrace(); } try { broadcast(buffer); } catch (IOException ex) { ex.printStackTrace(); } if (buffer != null) { buffer.clear(); buffer = null; } } private void broadcast(ByteBuffer buffer) throws IOException { buffer.flip(); Iterator iter = room.iterator(); while (iter.hasNext()) { SocketChannel sc = (SocketChannel) iter.next(); if (sc != null) { sc.write(buffer); buffer.rewind(); } } } } |
NIO 기반 클라이언트
package com.nio; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; import java.net.InetSocketAddress; import java.nio.ByteBuffer; import java.nio.channels.SelectionKey; import java.nio.channels.Selector; import java.nio.channels.SocketChannel; import java.nio.charset.CharacterCodingException; import java.nio.charset.Charset; import java.nio.charset.CharsetDecoder; import java.util.Iterator; import java.util.Scanner; import java.util.logging.FileHandler; import java.util.logging.Level; import java.util.logging.Logger; public class NioClient { static Selector selector = null; private SocketChannel sc = null; public void initServer() { try { selector = Selector.open(); sc = SocketChannel.open(new InetSocketAddress("127.0.0.1", 9999)); sc.configureBlocking(false); sc.register(selector, SelectionKey.OP_READ); } catch (IOException ex) { } } public void startServer() { initServer(); Receive rt = new Receive(); new Thread(rt).start(); startWriter(); } public void startWriter() { ByteBuffer buffer = ByteBuffer.allocateDirect(1024); try { while (true) { Scanner scanner = new Scanner(System.in); String message = scanner.next(); buffer.clear(); buffer.put(message.getBytes()); buffer.flip(); sc.write(buffer); } } catch (Exception ex) { } finally { clearBuffer(buffer); } } static void clearBuffer(ByteBuffer buffer) { if (buffer != null) { buffer.clear(); buffer = null; } } } class Receive implements Runnable { private Charset charset = null; private CharsetDecoder decoder = null; public void run() { charset = Charset.forName("EUC-KR"); decoder = charset.newDecoder(); try { while (true) { NioClient.selector.select(); Iterator it = NioClient.selector.selectedKeys().iterator(); while (it.hasNext()) { SelectionKey key = (SelectionKey) it.next(); if(key.isReadable()) { read(key); }else{} it.remove(); } } } catch (Exception ex) {} } private void read(SelectionKey key) { SocketChannel sc = (SocketChannel) key.channel(); ByteBuffer buffer = ByteBuffer.allocateDirect(1024); int nbyte=0; try { nbyte = sc.read(buffer); buffer.flip(); String data = decoder.decode(buffer).toString(); System.out.println("Receive Message - " + data); NioClient.clearBuffer(buffer); }catch (IOException ex){ try { sc.close(); } catch (IOException ex1) { } } } } |
'자바 > 자바팁' 카테고리의 다른 글
svn 관련 팁 (0) | 2012.04.05 |
---|---|
NIO 간단한 파일읽기 예제 (0) | 2012.01.19 |
자바 NIO 강좌 (0) | 2012.01.18 |
[자바팁]SequenceInputStream example (1) | 2012.01.11 |
자바 압축 관련 정보 (0) | 2012.01.10 |