NIO가 무엇일까요?  
NonBloking Input-Output 입니다. 기존의 일반 IO는 항상 블럭화가 되면서, 처리를 해왓습니다.
블럭화가되면 그블럭이 풀리기전까진 아무것도  수행할수 없지요. 그래서 그것이 큰 단점이기도 하지요.
NIO는 JDK 1.4 이전에는 존재 하지 않는 방법론입니다. 그 이후에 생긴 방법론이므로 참고 하시면 되구요.
어쨋든 그래서 NIO가 무엇이냐면.. 블럭화(잠수)를 타지않고 작업을 할수있게 해주는 방법론이에요.
서버가 존재하고 통신을 할때 메인스레드가 블럭화(잠수)를 타버리면 그 블럭화가 풀리기전까진 아무것도 못하잖아요..?
이것은 블럭을 시키지않고서, 누가 들어오던지 자료를 요청하던지 블럭 시키지않고 그냥 무작정 일만 처리시키는것이죠.

하나  이상적인 를 들어볼가요?

사장 : 어이 신입.. 내일까지 3천장 A4용지 복사좀 해놓게나...
신입 : 예!!

1시간후...

부장 : 어이 신입.. 미안한데말이야... 프로그래밍 유지보수가 생겨버렷어... 저 코드좀 내일까지 고쳐주게나...
신입 :  네 -_-;

과장 : 이보게나..  가족사진이 있는데 그지가치 나왓어 좀 고쳐준나..
신입 : 네 ㅜㅜ

위에서 신입이라는 사람이 있지요.. 지금 3가지 일을 부여 받았어요.
지금 신입이 해야되는일은  (전화받기,  복사하기,  코딩하기) 입니다.

이제 위의 상황을 IO와 NIO로 어떻게 처리할수있는지 비교해보겠씁니다.

1.  I/O 를 이용한 일처리
- 사장이 시킨 3천장 복사를 다한다. 그거다하기전까진 다 쌩간다.
- 그담에 부장이 시킨 코딩을 한다...
- 마지막 과장이 시킨 사진 작업을한다.

2. N/I/O를 이용한 일처리
- 복사기를 돌려놓고, 코딩두 하면서, 포토샵을 켜서 사진작업을합니다

무조건 다할대까지 하는것이 아니라..  계속 요청 들어오는데로 돌아가면서  하는것입니다.

NIO에서 추가적으로 생긴 개념은 Channel이라는 것과 Selector라는것이 있습니다.
채널은 스트림과같이 연결된 것들 끼리  읽기 쓰기 동시에 이루어질수록 하게 해주는것입니다.
일반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

+ Recent posts