2008. 12. 10. 17:59
jNetServer Socket Framework
2008. 12. 10. 17:59 in 개발/자바
jNetServer1.0 Readme
Network Server Application을 구현할 때 마다 느끼는 것이지만 맨날 반복되는 코드에 지겹다는 생각을 많이 했다. 물론 초보자에게는 Thread, Socket, IO 관련 프로그램 이 어렵게 느껴지겠고, 그리고, Thread Pool, Concurrency Issue등의 문제도 골치가 아플것이다. 아마도 이런 이유 때문에 application server를 쓰는지도 모르겠다. 하 지만 application server에서는 Socket Connector는 제공하지 않는다. 어째든 server application을 재활용하기 위한 차원에서 Socket Server Framework 을 만들어 보았다. 요즘 프로젝트가 다 웹이라 이런 Socket server를 얼마나 많이 개발 할지는 모르겠지만, 지금 참여하고 있는 프로젝트(EAI)에서는 중요한 부분으로 사용 되고 있다. (2003.11.23 일요일) |
|||||
written by Jeon HongSeong [hsjeon70@dreamwiz.com] | |||||
jNetServer1.0 Java Documentation API | |||||
1. 개 요 | |||||
◆ jNetServer1.0 개발 시 이용한 open source List | |||||
| |||||
◆ jNetServer Framework은 Java Network Server Programming에 대한 Basic Infrastructure를 제공한다. ◆ Network Server Application을 구현할 때 필요한 Thread Pooling이나, Object Pooling, Logger 등의 기능을 제공한다. ◆ API에 정의된 NetTask 내 클라이언트로부터 데이터를 read하고, 결과를 write하는 로직만 간단히 구현하면 된다. ◆ Configuration 설정만으로 Network Server Connector, InputAdapter, Object Pool이 생성 초기화 된다. ◆ 클라이언트 시스템(IP) 별로 접근 권한 및 통신 프로토콜을 다르게 정의할 수 있다. ◆ 모든 Java Object들이 JMX MBean object로 관리되고, MX4J에서 제공하는 JMX Http Admin 관리 콘솔을 제공한다. ◆ 관리 콘솔 상에서 서버 및 모든 MBean object를 제어 할 수 있다. ◆ JMX Monitor Bean을 이용해 Connector의 상태를 Counter, Gauge 방식으로 모니터 링 할 수 있다. ◆ jNetServer Framework 내에서 configuration 설정 만으로 SSL(Secure Socket Layer) 를 지원한다. SSL용 NetTask의 구현은 일반 Socket 일때와 동일하다. ◆ NetTask의 확장으로 프락시 서버, 로드 발런스 등의 서버를 쉽게 구현할 수 있다. | |||||
2. 설치 및 실행 | |||||
◆ jNetServer1.0-app.jar 파일을 c:\jNetServer1.0 디렉토리 밑에 압축을 해제한다. C:\jNetServer1.0>jar xvf jNetServer1.0-app.jar C:\jNetServer1.0>dir C 드라이브의 볼륨에는 이름이 없습니다. 볼륨 일련 번호: 3D26-12D4 C:\jNetServer1.0 디렉터리 2003-11-29 12:48a <DIR> . 2003-11-29 12:48a <DIR> .. 2003-11-29 12:48a 637 runSsl.bat 2003-11-29 12:48a 789 startup.bat 2003-11-29 12:48a 18 lcp.bat 2003-11-29 12:48a 705 runMulti.bat 2003-11-29 12:48a 720 stop.bat 2003-11-29 12:48a 645 runEcho.bat 2003-11-29 12:48a <DIR> logs 2003-11-29 12:48a <DIR> docs 2003-11-29 12:48a <DIR> server 2003-11-29 12:48a <DIR> common 2003-11-29 12:48a <DIR> config 6개 파일 3,514 바이트 7 디렉터리 2,378,612,736 바이트 남음 ◆ jNetServer1.0/config/server.xml에 Server 태그의 address 속성을 설치 시스템의 IP 주소로 변경한다. <ServerGroup> <Server info="jNetServer1.0" name="svr1" address="192.168.0.13" port="8110" mode="standalone" shutdown="SHUTDOWN"> ◆ startup.bat 파일을 실행하여 jNetServer를 start 시킨다. 이때 keystore file의 비밀번호를 입력해야 하는데 "java11"을 입력한다. C:\jNetServer1.0>startup CLASSPATH=.;D:\bea\weblogic81\server\lib\weblogic.jar;server\lib\jNetServer1.0.j ar;server\lib\jakarta\commons-configuration-0.8.1.jar;server\lib\jakarta\commons -digester.jar;server\lib\jakarta\commons-net-1.0.1-dev.jar;server\lib\jakarta\co mmons-daemon.jar;server\lib\jakarta\commons-collections.jar;server\lib\jakarta\c ommons-logging-api.jar;server\lib\jakarta\commons-beanutils.jar;server\lib\jakar ta\commons-logging.jar;server\lib\jakarta\commons-pool-1.1.jar;server\lib\jakart a\commons-dbcp.jar;server\lib\jakarta\commons-lang.jar;server\lib\log4j\log4j-1. 2.8.jar;server\lib\mx4j\mx4j-jmx.jar;server\lib\mx4j\mx4j-tools.jar;common\class es;config [00:51:29] INFO - @ jNetServer1.0 @@@@@@@@@@@@@@@@@@@@@@@@@@ [00:51:29] INFO - >> jnet.home=. [00:51:29] INFO - >> jnet.server=svr1 [00:51:29] INFO - >> Logger=jnet.logger Enter the password of the keystore file : java11 [00:54:12] INFO - >> Including directory C:\jNetServer1.0\.\common\classes [00:54:12] INFO - StandardConnector{7130} listen 192.168.0.13 [00:54:12] DEBUG - EchoTask#taskCreate()- EchoTask{echo1}[0] [00:54:12] DEBUG - EchoTask#taskPassivate()- EchoTask{echo1}[0] [00:54:12] DEBUG - EchoTask#taskCreate()- EchoTask{echo1}[1] [00:54:12] DEBUG - EchoTask#taskPassivate()- EchoTask{echo1}[1] [00:54:12] DEBUG - EchoTask#taskCreate()- EchoTask{echo1}[2] [00:54:12] DEBUG - EchoTask#taskPassivate()- EchoTask{echo1}[2] [00:54:12] INFO - StandardTask{echo1} started [00:54:12] INFO - StandardInputAdapter{everyone} started [00:54:12] INFO - StandardMonitor[idleHandlers] started [00:54:12] INFO - StandardMonitor[curHandlers] started [00:54:12] INFO - Handler{7130}[0] has been started [00:54:12] INFO - Handler{7130}[1] has been started [00:54:12] INFO - Handler{7130}[2] has been started [00:54:12] INFO - Handler{7130}[3] has been started [00:54:12] INFO - Handler{7130}[4] has been started [00:54:12] INFO - StandardConnector{7130} started [00:54:12] INFO - StandardConnector{7131} listen 192.168.0.13 [00:54:13] DEBUG - SSLProxyTask#taskCreate()- SSLProxyTask{proxy}[0] [00:54:13] DEBUG - SSLProxyTask#taskPassivate()- SSLProxyTask{proxy}[0] [00:54:13] DEBUG - SSLProxyTask#taskCreate()- SSLProxyTask{proxy}[1] [00:54:13] DEBUG - SSLProxyTask#taskPassivate()- SSLProxyTask{proxy}[1] [00:54:13] DEBUG - SSLProxyTask#taskCreate()- SSLProxyTask{proxy}[2] [00:54:13] DEBUG - SSLProxyTask#taskPassivate()- SSLProxyTask{proxy}[2] [00:54:13] DEBUG - SSLProxyTask#taskCreate()- SSLProxyTask{proxy}[3] [00:54:13] DEBUG - SSLProxyTask#taskPassivate()- SSLProxyTask{proxy}[3] [00:54:13] DEBUG - SSLProxyTask#taskCreate()- SSLProxyTask{proxy}[4] [00:54:13] DEBUG - SSLProxyTask#taskPassivate()- SSLProxyTask{proxy}[4] [00:54:13] INFO - StandardTask{proxy} started [00:54:13] INFO - StandardInputAdapter{192.168.0.13} started [00:54:13] DEBUG - SSLEchoTask#taskCreate()- SSLEchoTask{echo2}[0] [00:54:13] DEBUG - SSLEchoTask#taskPassivate()- SSLEchoTask{echo2}[0] [00:54:13] DEBUG - SSLEchoTask#taskCreate()- SSLEchoTask{echo2}[1] [00:54:13] DEBUG - SSLEchoTask#taskPassivate()- SSLEchoTask{echo2}[1] [00:54:13] DEBUG - SSLEchoTask#taskCreate()- SSLEchoTask{echo2}[2] [00:54:13] DEBUG - SSLEchoTask#taskPassivate()- SSLEchoTask{echo2}[2] [00:54:13] DEBUG - SSLEchoTask#taskCreate()- SSLEchoTask{echo2}[3] [00:54:13] DEBUG - SSLEchoTask#taskPassivate()- SSLEchoTask{echo2}[3] [00:54:13] DEBUG - SSLEchoTask#taskCreate()- SSLEchoTask{echo2}[4] [00:54:13] DEBUG - SSLEchoTask#taskPassivate()- SSLEchoTask{echo2}[4] [00:54:13] INFO - StandardTask{echo2} started [00:54:13] INFO - StandardInputAdapter{everyone} started [00:54:13] INFO - Handler{7131}[0] has been started [00:54:13] INFO - Handler{7131}[1] has been started [00:54:13] INFO - Handler{7131}[2] has been started [00:54:13] INFO - Handler{7131}[3] has been started [00:54:13] INFO - Handler{7131}[4] has been started [00:54:13] INFO - StandardConnector{7131} started [00:54:13] INFO - StandardServer{svr1} started [00:54:13] INFO - StandardServerGroup started [00:54:13] INFO - ConsoleGaugeListener>> MonitorNotification [ sequence=1, time Stamp=Sat Nov 29 00:54:13 KST 2003, type=jmx.monitor.gauge.low, userData=null, m essage=, derivedGauge=5, observedObject=jnet.server:name=svr1/con7130, observedA ttribute=IdleHandlers, trigger=5, source=javax.management.monitor.GaugeMonitor@c 623af ] [00:54:13] INFO - ConsoleCounterListener>> MonitorNotification [ sequence=1, ti meStamp=Sat Nov 29 00:54:13 KST 2003, type=jmx.monitor.counter.threshold, userDa ta=null, message=, derivedGauge=5, observedObject=jnet.server:name=svr1/con7130, observedAttribute=CurHandlers, trigger=3, source=javax.management.monitor.Count erMonitor@50ca0c ] | |||||
3. Admin Console | |||||
◆ jNetServer가 실행되면, Http Admin Console에 접근해 관리할수 있다. http://192.168.0.13:8080 브라우저로 8080 포트로 접근해 보면 아래와 같이 로그온 화면이 나타나는데, jlook/jlook으로 오그온을 한다. admin 계정과 비밀번호는 은 config/server.xml 에 설정되어 있다. ◆ 로그인하면 start된 jNetServer의 JMX MBean object를 관리할수 있는 기능과 상태 를 모니터링 할 수 있는 화면을 제공한다. | |||||
4. EchoTask Bean 및 테스트 | |||||
◆ 설치된 jNetServer에는 예제로 EchoTask Bean이 제공된다. Task Bean의 개발은 jlook.jnet.task.NetTask 인터페이스를 구현하면 된다. 다음은 NetTask의 소스이다. | |||||
5. SSL 설정 및 테스트 | |||||
◆ config/server.xml의 내용을 보면 다음과 같이 7131 포트가 SSL 로 설정되어 있다. | |||||
6. SSLProxyTask Beans | |||||
◆ NetTask 응용으로 Proxy Server Beans를 소개한다. 서버를 7130, SSL 7131로 서비스 할때 7131로 들어온 요청을 7130쪽으로 forwarding하는 SSLProxyTask의 소스와 설정을 살펴보자. 다음은 server.xml의 7131쪽 Connector 설정 부분인데, ssl="true"로 설정된 것을 확인할 수 있다. 그리고, 앞 예제와는 다르게 InputAdapter의 source가 현 시스템의 ip{192.168.0.13}로 변경된것을 확인할 수 있다. 즉, 192.168.0.13 시스템의 클라이 언트의 요청을 SSLProxyTask 가 처리한다는 것을 설정한 것이다. | |||||
| |||||
written by Jeon HongSeong |
2008. 12. 9. 11:42
eclipse TCP/IP 모니터 사용하기
2008. 12. 9. 11:42 in 개발/자바
파이어폭스의 파이어버그나 ie의 피들러 또는 상용인 httpwatch 등을 통해서 웹브라우저에서 일어나는 통신의 안 보이는 부분을 볼 수 있습니다. 헤더 영역의 정보 같은 것이죠.
이클립스 WTP에서도 같은 기능을 지원합니다. TCP/IP 모니터뷰를 이용하면 지정된 포트에서 발생하는 교신 정보를 눈으로 확인할 수 있습니다.
설정은 다음과 같이 Preferences 에서 tcp 필터 단어를 입력하면 메뉴가 보입니다.
Add 버튼을 클릭하고 상단의 Local Monitoring port 를 8090 등 사용하지 않는 포트를 입력합니다. 모니터링할 서비스 정보를 입력합니다. 로컬의 톰캣을 모니터링하기 위해서 localhost 포트는 8080 이라고 입력했습니다.
등록을 마치면 해당 모니터링 항목을 선택하고 우측의 Start 버튼을 클릭해서 모니터링을 시작합니다.
모니터링하는 포트로 호출을 합니다. 8090이라고 정했기 때문에 http://localhost:8090/index.jsp 주소로 접근하면 TCP/IP Monitor 뷰가 자동으로 뜨게 됩니다.
TCP/IP Monitor 탭을 더블클릭해서 크게 확대해서 보면 서버로 요청한 주소 목록과 선택한 주소의 헤더 정보와 주고받은 내용들이 하단에 나옵니다.
이미지를 선택하면 해당 이미지도 볼 수 있습니다.
유용하게 쓰시기 바랍니다.
2008. 12. 8. 10:38
[JAVA] NIO 를 이용한 Echo Server / Client
2008. 12. 8. 10:38 in 개발/자바
모든 네트워크 프로그래밍의 시작은 Echo 인듯 ...
어디서 긁어서 테스트 해본 소스라 올리기 힘듬
크크 누가 들어올라만은...
[ EchoClient.java ]
import java.io.*;
import java.net.*;
class EchoClient
{ public static void main( String[] args )
throws IOException
{
Socket sock = null;
try
{
sock = new Socket(args[0], Integer.parseInt(args[1]));
System.out.println(sock + ": 연결됨");
OutputStream toServer = sock.getOutputStream();
InputStream fromServer = sock.getInputStream();
byte[] buf = new byte[1024];
int count;
while( (count = System.in.read(buf)) != -1 )
{
toServer.write( buf, 0, count );
count = fromServer.read( buf );
System.out.write( buf, 0, count );
}
toServer.close();
while((count = fromServer.read(buf)) != -1 )
System.out.write( buf, 0, count );
System.out.close();
System.out.println(sock + ": 연결 종료");
} catch( IOException ex )
{
System.out.println("연결 종료 (" + ex + ")");
} finally
{
try
{
if ( sock != null )
sock.close();
} catch( IOException ex ) {}
}
}
}
///////////////////////////////////////////////////////////////////////////////////////////////////
[ EchoServer.java ]
import java.nio.channels.*;
import java.net.InetAddress;
import java.nio.ByteBuffer;
import java.net.InetSocketAddress;
import java.net.ServerSocket;
import java.net.SocketAddress;
import java.util.Set;
import java.util.Iterator;
import java.io.IOException;
import java.nio.charset.Charset;
import java.nio.charset.CharsetEncoder;
import java.nio.CharBuffer;
public class NIOServer implements Runnable {
//어떤 채널이 어떤 IO를 할 수 있는지 알려주는 클래스(Seelctor)
Selector selector;
int port = 9999;
//한글 전송용
Charset charset = Charset.forName("EUC-KR");
CharsetEncoder encoder = charset.newEncoder();
public NIOServer() throws IOException {
//Selector를 생성 합니다.
selector = Selector.open();
//ServerSocket에 대응하는 ServerSocketChannel을 생성, 아직 바인딩은 안됨
ServerSocketChannel channel = ServerSocketChannel.open();
//서버 소켓 생성
ServerSocket socket = channel.socket();
SocketAddress addr = new InetSocketAddress(port);
//소켓을 해당 포트로 바인딩
socket.bind(addr);
//Non-Blocking 상태로 만듬
channel.configureBlocking(false);
//바인딩된 ServerSocketChannel을 Selector에 등록 합니다.
channel.register(selector, SelectionKey.OP_ACCEPT);
System.out.println("---- Client의 접속을 기다립니다... ----");
}
public void run() {
//SocketChannel용 변수를 미리 만들어 둡니다.
int socketOps = SelectionKey.OP_CONNECT | SelectionKey.OP_READ | SelectionKey.OP_WRITE;
ByteBuffer buff = null;
try {
//생성된 서버소켓채널에 대해 accept 상태 일때 알려달라고 selector에 등록 시킨 후
//이벤트가 일어날때 까지 기다립니다. 새로운 클라이언트가 접속하면 seletor는
//미리 등록 했던 SeerverSocketChannel에 이벤트가 발생했으므로 select 메소드에서
//1을 돌려줍니다. 즉 Selector에 감지된 이벤트가 있다면
while(selector.select() > 0) {
//현재 selector에 등록된 채널에 동작이 라나라도 실행 되는 경우 그 채널들을 SelectionKey의
//Set에 추가 합니다. 아래에서는 선택된 채널들의 키를 얻습니다. 즉 해당 IO에 대해 등록해
//놓은 채널의 키를 얻는 겁니다.
Set keys = selector.selectedKeys();
Iterator iter = keys.iterator();
while(iter.hasNext()) {
SelectionKey selected = (SelectionKey)iter.next();
//현재 처리하는 SelectionKey는 Set에서 제거 합니다.
iter.remove();
//channel()의 현재 하고 있는 동작(읽기, 쓰기)에 대한 파악을 하기 위한 겁니다.
SelectableChannel channel = selected.channel();
if(channel instanceof ServerSocketChannel) {
//ServerSocketChannel이라면 accept()를 호출해서
//접속 요청을 해온 상대방 소켓과 연결 될 수 있는 SocketChannel을 얻습니다.
ServerSocketChannel serverChannel = (ServerSocketChannel) channel;
SocketChannel socketChannel = serverChannel.accept();
//현시점의 ServerSocketChannel은 Non-Blocking IO로 설정 되어 있습니다.
//이것은 당장 접속이 없어도 블로킹 되지 않고 바로 null을 던지므로
//체트 해야 합니다.
if (socketChannel == null ){
System.out.println("## null server socket");
continue;
}
System.out.println("## socket accepted : " + socketChannel);
//얻어진 소켓은 블로킹 소켓이므로 Non-Blocking IO 상태로 설정 합니다.
socketChannel.configureBlocking(false);
//소켓 채널을 Selector에 등록
socketChannel.register(selector, socketOps);
}
else {
//일반 소켓 채널인 경우 해당 채널을 얻어낸다.
SocketChannel socketChannel = (SocketChannel) channel;
buff = ByteBuffer.allocate(100);
//소켓 채널의 행동을 검사해서 그에 대응하는 작업을 함
if (selected.isConnectable()){
System.out.println("Client와의 연결 설정 OK~");
if (socketChannel.isConnectionPending()){
System.out.println("Client와의 연결 설정을 마무리 중입니다~");
socketChannel.finishConnect();
}
}
//읽기 요청 이라면
else if(selected.isReadable()) {
//소켓 채널로 데이터를 읽어 들입니다.
try {
socketChannel.read(buff);
//데이터가 있다면
if (buff.position() != 0){
buff.clear();
System.out.print("클라이언트로 전달된 내용 : ");
//Non-Blocking Mode이므로 데이터가 모두 전달될때 까지 기다림
while(buff.hasRemaining()) {
System.out.print((char) buff.get());
}
buff.clear();
System.out.println();
//쓰기가 가능 하다면
if (selected.isWritable()){
String str = "이건 서버에서 보낸 데이터...";
//한글 인코딩
socketChannel.write(encoder.encode(CharBuffer.wrap(str + "
" )));
System.out.println("서버가 전달한 내용 : " + str);
}
}
}
catch (IOException ioe)
{
ioe.printStackTrace();
socketChannel.finishConnect();
socketChannel.close();
}
}
}
}
}
}
catch (Exception e)
{
e.printStackTrace();
}
}
public static void main(String[] args)throws IOException {
NIOServer s = new NIOServer();
new Thread(s).start();
}
}
어디서 긁어서 테스트 해본 소스라 올리기 힘듬
크크 누가 들어올라만은...
[ EchoClient.java ]
import java.io.*;
import java.net.*;
class EchoClient
{ public static void main( String[] args )
throws IOException
{
Socket sock = null;
try
{
sock = new Socket(args[0], Integer.parseInt(args[1]));
System.out.println(sock + ": 연결됨");
OutputStream toServer = sock.getOutputStream();
InputStream fromServer = sock.getInputStream();
byte[] buf = new byte[1024];
int count;
while( (count = System.in.read(buf)) != -1 )
{
toServer.write( buf, 0, count );
count = fromServer.read( buf );
System.out.write( buf, 0, count );
}
toServer.close();
while((count = fromServer.read(buf)) != -1 )
System.out.write( buf, 0, count );
System.out.close();
System.out.println(sock + ": 연결 종료");
} catch( IOException ex )
{
System.out.println("연결 종료 (" + ex + ")");
} finally
{
try
{
if ( sock != null )
sock.close();
} catch( IOException ex ) {}
}
}
}
///////////////////////////////////////////////////////////////////////////////////////////////////
[ EchoServer.java ]
import java.nio.channels.*;
import java.net.InetAddress;
import java.nio.ByteBuffer;
import java.net.InetSocketAddress;
import java.net.ServerSocket;
import java.net.SocketAddress;
import java.util.Set;
import java.util.Iterator;
import java.io.IOException;
import java.nio.charset.Charset;
import java.nio.charset.CharsetEncoder;
import java.nio.CharBuffer;
public class NIOServer implements Runnable {
//어떤 채널이 어떤 IO를 할 수 있는지 알려주는 클래스(Seelctor)
Selector selector;
int port = 9999;
//한글 전송용
Charset charset = Charset.forName("EUC-KR");
CharsetEncoder encoder = charset.newEncoder();
public NIOServer() throws IOException {
//Selector를 생성 합니다.
selector = Selector.open();
//ServerSocket에 대응하는 ServerSocketChannel을 생성, 아직 바인딩은 안됨
ServerSocketChannel channel = ServerSocketChannel.open();
//서버 소켓 생성
ServerSocket socket = channel.socket();
SocketAddress addr = new InetSocketAddress(port);
//소켓을 해당 포트로 바인딩
socket.bind(addr);
//Non-Blocking 상태로 만듬
channel.configureBlocking(false);
//바인딩된 ServerSocketChannel을 Selector에 등록 합니다.
channel.register(selector, SelectionKey.OP_ACCEPT);
System.out.println("---- Client의 접속을 기다립니다... ----");
}
public void run() {
//SocketChannel용 변수를 미리 만들어 둡니다.
int socketOps = SelectionKey.OP_CONNECT | SelectionKey.OP_READ | SelectionKey.OP_WRITE;
ByteBuffer buff = null;
try {
//생성된 서버소켓채널에 대해 accept 상태 일때 알려달라고 selector에 등록 시킨 후
//이벤트가 일어날때 까지 기다립니다. 새로운 클라이언트가 접속하면 seletor는
//미리 등록 했던 SeerverSocketChannel에 이벤트가 발생했으므로 select 메소드에서
//1을 돌려줍니다. 즉 Selector에 감지된 이벤트가 있다면
while(selector.select() > 0) {
//현재 selector에 등록된 채널에 동작이 라나라도 실행 되는 경우 그 채널들을 SelectionKey의
//Set에 추가 합니다. 아래에서는 선택된 채널들의 키를 얻습니다. 즉 해당 IO에 대해 등록해
//놓은 채널의 키를 얻는 겁니다.
Set keys = selector.selectedKeys();
Iterator iter = keys.iterator();
while(iter.hasNext()) {
SelectionKey selected = (SelectionKey)iter.next();
//현재 처리하는 SelectionKey는 Set에서 제거 합니다.
iter.remove();
//channel()의 현재 하고 있는 동작(읽기, 쓰기)에 대한 파악을 하기 위한 겁니다.
SelectableChannel channel = selected.channel();
if(channel instanceof ServerSocketChannel) {
//ServerSocketChannel이라면 accept()를 호출해서
//접속 요청을 해온 상대방 소켓과 연결 될 수 있는 SocketChannel을 얻습니다.
ServerSocketChannel serverChannel = (ServerSocketChannel) channel;
SocketChannel socketChannel = serverChannel.accept();
//현시점의 ServerSocketChannel은 Non-Blocking IO로 설정 되어 있습니다.
//이것은 당장 접속이 없어도 블로킹 되지 않고 바로 null을 던지므로
//체트 해야 합니다.
if (socketChannel == null ){
System.out.println("## null server socket");
continue;
}
System.out.println("## socket accepted : " + socketChannel);
//얻어진 소켓은 블로킹 소켓이므로 Non-Blocking IO 상태로 설정 합니다.
socketChannel.configureBlocking(false);
//소켓 채널을 Selector에 등록
socketChannel.register(selector, socketOps);
}
else {
//일반 소켓 채널인 경우 해당 채널을 얻어낸다.
SocketChannel socketChannel = (SocketChannel) channel;
buff = ByteBuffer.allocate(100);
//소켓 채널의 행동을 검사해서 그에 대응하는 작업을 함
if (selected.isConnectable()){
System.out.println("Client와의 연결 설정 OK~");
if (socketChannel.isConnectionPending()){
System.out.println("Client와의 연결 설정을 마무리 중입니다~");
socketChannel.finishConnect();
}
}
//읽기 요청 이라면
else if(selected.isReadable()) {
//소켓 채널로 데이터를 읽어 들입니다.
try {
socketChannel.read(buff);
//데이터가 있다면
if (buff.position() != 0){
buff.clear();
System.out.print("클라이언트로 전달된 내용 : ");
//Non-Blocking Mode이므로 데이터가 모두 전달될때 까지 기다림
while(buff.hasRemaining()) {
System.out.print((char) buff.get());
}
buff.clear();
System.out.println();
//쓰기가 가능 하다면
if (selected.isWritable()){
String str = "이건 서버에서 보낸 데이터...";
//한글 인코딩
socketChannel.write(encoder.encode(CharBuffer.wrap(str + "
" )));
System.out.println("서버가 전달한 내용 : " + str);
}
}
}
catch (IOException ioe)
{
ioe.printStackTrace();
socketChannel.finishConnect();
socketChannel.close();
}
}
}
}
}
}
catch (Exception e)
{
e.printStackTrace();
}
}
public static void main(String[] args)throws IOException {
NIOServer s = new NIOServer();
new Thread(s).start();
}
}