2016년 7월 22일 금요일

JDK 1.4 튜토리얼 - 2장 NIO- 논블록킹-Polling

2.3 넌블록킹 I/O

JDK1.4 이전에는 java.io에서는 non-blocking(상세) 을 지원하지 않음
특징
  • 작업 종료 후 응답이 돌아오기 전에 다음 작업 수행할 수 있음
  • 많은 수의 I/O 커넥션을 관리시 오버헤드를 감소 시킴(**)
  • C/S 서버 환경에서 좋은 성능을 발휘 함
  • Non-Blocking 기법은
    • Polling 방식 - 주기적으로 확인
    • Multiplexing 방식 - select를 사용하는 event 방식
다중 쓰레드를 사용함(Blocking 방식)
  • 단점
    • 많은 쓰레드 자원을 소비 한다.
  • 장점
    • 구조를 단순화 할 수 있음
  • 만약 다중 쓰레드를 단일 쓰레드 방식으로 변경하면 특정 통신에 이상 발생하면 나머지 모든 멈추는 현상이 발생 하게 된다.
Polling(폴링)
  • 단점
    • 폴링 대기시간이 존재하면 결국 대기 시간을 줄이게 되면 부하가 상승함.
    • 매우 짧고 높은 응답 성능을 요구하는 서버에서는 사용이 적절하지 않음

package ns.jdk14;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.net.ServerSocket;
import java.net.Socket;
import java.nio.ByteBuffer;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.util.*;

public class PollingChatServer implements Runnable{
   private static final int sleepTime = 100; //SLEEP_TIME
   private int port;   //포트번호
   private Vector<Socket> sockets = new Vector<>();
   private Set<Socket> closedSockets = new HashSet<>();

   public PollingChatServer(int port) {
       this.port = port;
       Thread t = new Thread(this,"PollingChatServer");
       t.start();
   }

   @Override
   public void run() {
       try{
           //1) 논블록킹 서버 소켓 OPEN
           ServerSocketChannel ssc = ServerSocketChannel.open();
           ssc.configureBlocking(false);
           ServerSocket ss = ssc.socket();
           InetSocketAddress isa = new InetSocketAddress(port);
           ss.bind(isa);

           ByteBuffer buffer = ByteBuffer.allocate(4096);
           System.out.println("Listening on port "+port);
           while(true){
               //2) 서버 소켓에 새로운 연결이 들어옴
               SocketChannel sc = ssc.accept();
               //연결이 있는 경우에는 not null 이고 null 이면 없는 경우 이다.
               if(sc != null){
                   Socket newSocket = sc.socket();
                   System.out.println("Connection from "+newSocket);
                   //논블록킹으로 설정    
                   newSocket.getChannel().configureBlocking(false);
                   sockets.addElement(newSocket);
               }
               //3) 클라이언트 소켓중의 하나로 데이터가 읽혀짐
               for(Enumeration<Socket> e = sockets.elements();
                   e.hasMoreElements();){
                   Socket socket = null;
                   try{
                       socket = e.nextElement();
                       SocketChannel sch = socket.getChannel();
                       buffer.clear();
                       sch.read(buffer);
                       //Data가 있는 경우
                       if(buffer.position() > 0){
                           buffer.flip();
                           System.out.println("Read "+buffer.limit()
                                   +" bytes from "+sch.socket());
                           sendTOAll(buffer);
                       }

                   }catch(IOException ie){
                       closedSockets.add(socket);
                   }
               }

               removeClosedSockets();
               try {
                   Thread.sleep(sleepTime);
               }catch(InterruptedException ie){}
           }
       }catch(IOException e){e.printStackTrace();}
   }
   /**
    * 4) Data 클라이언트 소켓에
    * @param bb
    */
   private void sendTOAll(ByteBuffer bb){
       for(Enumeration<Socket> e = sockets.elements();
           e.hasMoreElements();){
           Socket socket = null;

           try{
               socket = e.nextElement();
               SocketChannel sc = socket.getChannel();
               //버퍼의 포지션을 시작 위치로 변경함.
               bb.rewind();
               //보낼것이 있는 경우
               while(bb.remaining() > 0){
                   sc.write(bb);
               }
           }catch(IOException ie){
               closedSockets.add(socket);
           }
       }
   }
   /**
    * 5) 연결 종료된 소켓 제거
    */
   private void removeClosedSockets(){
       for(Iterator<Socket> it=closedSockets.iterator();it.hasNext();){
           Socket socket = it.next();
           sockets.remove(socket);
           System.out.println("Removed "+socket);
       }
       closedSockets.clear();
   }
   public static void main(String[] args) {
       //Integer.parseInt(args[0]);
       int port = 5555;
       new PollingChatServer(port);
   }
}

출처 : 인포북 JDK 1.4 Tutorial