2.3 넌블록킹 I/O
특징
- 작업 종료 후 응답이 돌아오기 전에 다음 작업 수행할 수 있음
- 많은 수의 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