Site Search :
Standard Enterprise XML Methodology Pattern Setting Tunning Other
Article Contributors
GuestBook
Javapattern Maven
XSourceGen Dev
JetSpeed Test
JLook Image
jLook Family Site


jNetServer Socket Framework
 
server application을 재활용하기 위한 차원에서 Socket Server Framework 을 만들어 보았다. 요즘 프로젝트가 다 웹이라 이런 Socket server를 얼마나 많이 개발 할지는 모르겠지만, 지금 참여하고 있는 프로젝트(EAI)에서는 중요한 부분으로 사용 되고 있다. ( 2003/12/01 ) 350
Written by specular - 전홍성
1 of 1
 

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
- MX4J-1.1 : JMX1.0 Reference Implementation - Jakarta Log4J-1.2.8 - Jakarta Common Digester-1.5 - Jakarta Common Pooling-1.1 - Jakarta Tomcat source 일부
 

 ◆ 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의 
    소스이다.       
    
     package jlook.jnet.task;
     
     import java.io.IOException;
     import java.io.BufferedInputStream;
     import java.io.BufferedOutputStream;
     
     /**
      * Socket 요청을 받아 처리할 Beans를 구정의하기 위한 NetTask interface 
      *
      * @since 	jNetServer1.0
      * @author 	HongSeong Jeon(hsjeon70@dreamwiz.com)
      */
     public interface NetTask {
     	/**
     	 * NetTask object의 id를 반환한다.
     	 *	
     	 * @return	id
     	 */
     	public String getId();	
     		
     	/**
     	 * NetTask Beans object 생성 후 callback
     	 */
     	public void taskCreate();
     	
     	/**
     	 * NetTask Beans object가 요청에 의해 pooling으로 부터 나와 할당된 
     	 * 후 callback
     	 */
     	public void taskActivate();
     	
     	/**
     	 * NetTask Beans object가 요청을 처리하고, pooling으로 반환된 후 
     	 * callback
     	 */
     	public void taskPassivate();
     	
     	/**
     	 * NetTask Beans object가 pooling으로 부터 deallocated 될때 callback
     	 */
     	public void taskDestroy();
     	
     	
     	/**
     	 * 클라이언트의 요청을 받아 NetContext object를 초기화하기 위해 
     	 * 실행된다.
     	 *
     	 * @param	context	NetContext object
     	 */
     	public void setContext(NetContext context);
     	
     	/**
     	 * 클라이언트의 요청에 대한 처리로직을 위해 실행된다.
     	 *	
     	 * @param	in	BufferedInputStream object
     	 * @param	out	BufferedOutputStream object
     	 * @exception	TaskException
     	 */
     	public void doTask(BufferedInputStream in, BufferedOutputStream out)
     	throws TaskException;
     	
     	/**
     	 * 클라이언트의 요청에 대한 처리로직을 실행한 후 후처리 작업을 
     	 * 위해 실행된다.
     	 *
     	 * @param	success	doTask() 메서드에서 Exception의 발생여부를 
     	 * 		나타내는 flag
     	 */
     	public void doEnd(boolean success);
     }
- taskCreate() : Task Bean이 생성되고, Bean의 초기화 작업을 위해 호출된다. - taskDestory() : Task Bean이 Object Pool에서 삭제될 때 초기화 작업의 undo를 위해 호출된다. - taskActivate() : Task Bean이 Object Pool에 반환될때 호출된다. - taskPassivate() : Task Bean이 Object Pool로 부터 나와 서비스 되기 직전에 호출된다. - setContext() : 클라이언트의 요청 시 클라이언트의 정보를 갖는 Context 정보를 초기화 하기 위해 호출된다.\ - doTask() : 클라이언트 요청시 setContext() 가 실행된 다음 호출된다. 파라미터의 InputStream, OutputStream을 이용해 실재 해당 요청의 처리로직을 구현한다. - doEnd() : 후처리 작업을 위해 마지막에 호출된다. ◆ NetTask의 구현은 실재 NetTaskSupport 클래스를 상속받아 정의한다. 다음은 제공 되는 EchoTask의 소스이다.
    
     package jlook.jnet.task;
     
     import java.io.*;
     
     import jlook.jnet.Keys;
     import jlook.jnet.util.Logger;
     
     public class EchoTask extends NetTaskSupport {
     	private static Logger logger = Logger.getLogger(Keys.LOGGER);
     	
     	public void taskCreate() {
     		logger.debug("EchoTask#taskCreate()- "+id);	
     	}
     	
     	public void taskActivate(){
     		logger.debug("EchoTask#taskActivate()- "+id);	
     	}
     	
     	public void taskPassivate(){
     		logger.debug("EchoTask#taskPassivate()- "+id);	
     	}
     	
     	public void taskDestroy(){
     		logger.debug("EchoTask#taskDestroy()- "+id);	
     	}
     	
     	public void setContext(NetContext context) {
     		super.setContext(context);
     		logger.debug("EchoTask#setContext()- "+id);	
     	}
     	
     	public void doTask(BufferedInputStream in, BufferedOutputStream out)
     	throws TaskException {
     		Logger logger = context.getLogger();
     		logger.debug("EchoTask#doTask()- "+id);	
     		byte[] msg = new byte[100];
     		logger.debug(id+"#doTask()- read....");
     		
     		try {
 		   int len = in.read(msg);
 		   String str = new String(msg,0,len);
 	
 		   logger.debug(id+"#doTask()- received msg>> "+str);
 		   str = "Hi... "+str;
 		   byte[] rt = str.getBytes();
 		   out.write(rt, 0, rt.length);
     		} catch(Exception e) {
     		   throw new TaskException(e.getMessage());
     		}
     	}	
     	
     	public void doEnd(boolean success) {
     		logger.debug("EchoTask#doEnd()- "+id);	
     		
     	}
     	
     	public String toString() {
     		return "SampleNetTask>>"+id;	
     	}
     }
◆ 개발된 NetTask Bean은 config/server.xml에 다음과 같이 설정해야 한다.
    
     <InputAdapter source="everyone" 		
     		 connectionTimeout="10000" 	
     		 sendBufferSize="10240"
     		 receiveBufferSize="10240"
     	  	 className="jlook.jnet.connector.StandardInputAdapter">     
     	<Task 	name="echo1"	initSize="5"	maxSize="50"
     		className="jlook.jnet.connector.StandardTask"
     		beanClass="jlook.jnet.task.EchoTask"/>
     			
    </InputAdapter>
- name : unique name - initSize : object pool의 initial size - maxSize : object pool의 maximum size - className : Tag에 대한 정보를 갖는 Bean으로 위와 같이 반드시 제공되는 StandardTask 클래스를 설정한다. - beanClass : 개발한 NetTask Bean class를 설정한다. ◆ 다음은 EchoTask에 대한 Client 프로그램이다.
    
     package jlook.jnet.task;
     
     import java.io.*;
     import java.net.*;
     
     public class EchoClient implements Runnable {                            
     	private String server;
     	private int    port;
     	private String message;
     	
     	private boolean success;
     	private Socket sck;
     		
     	public EchoClient(String server, int port, String message) 
     	throws Exception {
	   this.server = server;
	   this.port = port;
	   this.message = message;	
	   try {
	   	sck = new Socket(server, port);
	   } catch(Exception e) {
	   	System.out.println(Thread.currentThread().getName() + 
	   	" Error>> "+e.toString());
	   	sck = new Socket(server, port);
	   }
     	}
     	
     	public static void main(String[] args) throws Exception {
	   if(args.length!=3) {
	   	System.out.println("usage : java EchoClient "+
	   	"  ");
	   	return;
	   }
	   
	   int port = -1;
	   try {
	   	port = Integer.parseInt(args[1]);
	   } catch(Exception e) {
	   	System.out.println("invalid port - "+port);
	   	throw e;
	   }
	   
	   EchoClient ec = new EchoClient(args[0], port, args[2]);
	   Thread t = new Thread(ec);
	   t.start();
     	}
     	
     	public boolean isSuccess() {
     		return success;	
     	}
     	
     	public void run() {
   	   InputStream in = null;
   	   OutputStream out = null;
   	   try {
   	     in = sck.getInputStream();
   	     out = sck.getOutputStream();
   	     	
   	     byte[] b = message.getBytes();
   	     out.write(b, 0, b.length);
   	     	
   	     byte[] buff = new byte[100];
   	     int len = in.read(buff);
   	     
   	     System.out.println(Thread.currentThread().getName()+
   	     "] "+ new String(buff, 0, buff.length));
   	     success = true;
   	     } catch(Exception e) {
   	     success = false;
   	     System.out.println(Thread.currentThread().getName() + 
   	     " Error>> "+e.toString());
   	     e.printStackTrace();
   	   } finally {
   	     try { notifyAll();} catch(Exception e){}	
   	     try { if(in!=null) in.close();} catch(Exception e) {}
   	     try { if(out!=null) out.close();} catch(Exception e) {}
   	     try { if(sck!=null) sck.close();} catch(Exception e) {}
   	   }
     	}
     }    
◆ 위 EchoClient를 테스트 하기 위해 runEcho.bat 파일 내 ip를 수정하고, 다음과 같이 runEcho.bat를 실행한다. C:\jNetServer1.0>runEcho CLASSPATH=.;D:\bea\weblogic81\server\lib\weblogic.jar;lib\jNetServer1.0.jar; classes;config Thread-1] Hi... hongseong ◆ 다음은 jNetServer쪽 콘솔에 나타난 로그 내용이다. 이 로그를 통해 NetTask에 정의된 메서드가 언제 실행되는지 이해할 수 있다. [21:32:48] DEBUG - @ request client address : 192.168.0.13 [21:32:48] DEBUG - StandardInputAdapter#doExecute() is started [21:32:48] DEBUG - EchoTask#taskActivate()- EchoTask{echo1}[4] [21:32:48] DEBUG - EchoTask#setContext()- EchoTask{echo1}[4] [21:32:48] DEBUG - EchoTask#doTask()- EchoTask{echo1}[4] [21:32:48] DEBUG - EchoTask{echo1}[4]#doTask()- read.... [21:32:48] DEBUG - EchoTask{echo1}[4]#doTask()- received msg>> hongseong [21:32:48] DEBUG - EchoTask#doEnd()- EchoTask{echo1}[4] [21:32:48] DEBUG - EchoTask#taskPassivate()- EchoTask{echo1}[4] [21:32:49] DEBUG - Handler{7130}[4] process time : 0.38 sec
 

5. SSL 설정 및 테스트

 
 ◆ config/server.xml의 내용을 보면 다음과 같이 7131 포트가 SSL 로 설정되어 있다.	
	
    
<Connector name="con7131" className="jlook.jnet.connector.StandardConnector" 
	    	port="7131" 		ssl="true"
	        enableLookups="true" acceptCount="50"
	        minHandlers="5" 		maxHandlers="50">
	<InputAdapter 	source="everyone" 		
		connectionTimeout="10000" 	
		sendBufferSize="10240"
		receiveBufferSize="10240"
	  	className="jlook.jnet.connector.StandardInputAdapter">
		
		<Task 	name="echo2" initSize="5" maxSize="50"
		  className="jlook.jnet.connector.StandardTask"
		  beanClass="jlook.jnet.task.SSLEchoTask"/>
				
	</InputAdapter>		
	
	<InputAdapter 	source="192.168.0.10" 		
		connectionTimeout="10000" 	
		sendBufferSize="10240"
		receiveBufferSize="10240"
	  	className="jlook.jnet.connector.StandardInputAdapter">
		
		<Task 	name="proxy" initSize="5" maxSize="50"
		  className="jlook.jnet.connector.StandardTask"
		  beanClass="jlook.jnet.task.SSLProxyTask">
		  <Parameter name="target.server" value="192.168.0.13"/>
		  <Parameter name="target.port"	 value="7130"/>
		  <Parameter name="buffer.size"	 value="9"/>
		</Task>
						
	</InputAdapter>
◆ 7131이 SSL로 설정되어 있으므로 앞에서 실행했던 runEcho.bat 파일의 port를 7131로 수정 후 실행해 보면 다음과 같이 에러가 발생한다. ### Client console] C:\jNetServer1.0>runEcho CLASSPATH=.;D:\bea\weblogic81\server\lib\weblogic.jar;server\lib\jNetServer1.0.j ar;common\classes;config Thread-1]  ### jNetServer console] [01:16:24] ERROR - Unrecognized SSL message, plaintext connection? javax.net.ssl.SSLException: Unrecognized SSL message, plaintext connection? at com.sun.net.ssl.internal.ssl.InputRecord.b(DashoA6275) at com.sun.net.ssl.internal.ssl.InputRecord.read(DashoA6275) at com.sun.net.ssl.internal.ssl.SSLSocketImpl.a(DashoA6275) at com.sun.net.ssl.internal.ssl.SSLSocketImpl.j(DashoA6275) at com.sun.net.ssl.internal.ssl.SSLSocketImpl.startHandshake(DashoA6275) ◆ runssl.bat 파일을 오픈해 ip{192.168.0.13}를 시스템에 맞게 변경하고, 실행해 보면 다음과 같은 결과를 확인해 볼수 있다. ### Client console] C:\jNetServer1.0>runssl CLASSPATH=.;D:\bea\weblogic81\server\lib\weblogic.jar;server\lib\jNetServer1.0.j ar;common\classes;config connect to the server.[192.168.0.13, 7131] received message>> Hello hongseong ### jNetServer console] [01:27:12] DEBUG - @ request client address : 192.168.0.13 [01:27:12] DEBUG - StandardInputAdapter#doExecute() is started [01:27:12] DEBUG - InputAdapter>>svr1/con7131/everyone [01:27:12] DEBUG - StandardInputAdapter#doExecute()- start handshake [01:27:12] DEBUG - SSLEchoTask#taskActivate()- SSLEchoTask{echo2}[4] [01:27:12] DEBUG - SSLEchoTask#setContext()- SSLEchoTask{echo2}[4] [01:27:12] DEBUG - SSLEchoTask#doTask()- SSLEchoTask{echo2}[4] [01:27:12] DEBUG - SSLEchoTask{echo2}[4]#doTask()- read.... [01:27:12] DEBUG - SSLEchoTask{echo2}[4]#doTask()- received msg>> hongseong [01:27:12] DEBUG - SSLEchoTask#taskPassivate()- SSLEchoTask{echo2}[4] [01:27:12] DEBUG - Handler{7131}[4] process time : 0.431 sec ◆ 클라이언트 시스템이 192.168.0.13 이므로 클라이언트 시스템에 대하 InputAdapter가 설정되어 있지 않기 때문에 default인 "everyone"의 InputAdapter가 동작한 것이다. 그리고, Task beans는 SSLEchoTask가 실행되었고, 그 소스는 아래와 같다. SSL 관련 추가 코드는 필요하지 않다.
package jlook.jnet.task;

import java.io.*;

import jlook.jnet.Keys;
import jlook.jnet.util.Logger;

public class SSLEchoTask extends NetTaskSupport {
	private static Logger logger = Logger.getLogger(Keys.LOGGER);
	
	public void taskCreate() {
		logger.debug("SSLEchoTask#taskCreate()- "+id);	
	}
	
	public void taskActivate(){
		logger.debug("SSLEchoTask#taskActivate()- "+id);	
	}
	
	public void taskPassivate(){
		logger.debug("SSLEchoTask#taskPassivate()- "+id);	
	}
	
	public void taskDestroy(){
		logger.debug("SSLEchoTask#taskDestroy()- "+id);	
	}
	
	public void setContext(NetContext context) {
		super.setContext(context);
		logger.debug("SSLEchoTask#setContext()- "+id);	
	}
	
	public void doTask(BufferedInputStream in, BufferedOutputStream out) 
	throws TaskException {
		Logger logger = context.getLogger();
		logger.debug("SSLEchoTask#doTask()- "+id);	
		byte[] msg = new byte[100];
		logger.debug(id+"#doTask()- read....");
		
		try {
			int len = in.read(msg);
			String str = new String(msg,0,len);
		
			logger.debug(id+"#doTask()- received msg>> "+str);
			str = "Hi... "+str;
			byte[] rt = str.getBytes();
			out.write(rt, 0, rt.length);
		} catch(Exception e) {
			logger.error(e.getMessage(),e);
			throw new TaskException(e.getMessage());
		}
	}	
	
	public void doEnd(boolean success) {
		logger.debug("SSLEchoTask#doEnd()- "+id);	
		
	}
	
	public String toString() {
		return "SampleNetTask>>"+id;	
	}
}
◆ 다음은 위에서 테스트한 ssl Client application 소스이다. 서버와 동일한 keystore 파일을 이용한 것을 알 수 있다.
package jlook.jnet.task;

import java.io.*;
import java.net.*;
import java.security.*;
import javax.net.*;
import javax.net.ssl.*;

public class SSLEchoClient {
	
  public static void main(String[] args) throws Exception {
	  System.setProperty("javax.net.ssl.trustStore", 
	    "config/jnet.keystore"); 
	  
	  if(args.length!=2) {
	  	System.out.println("java SSLEchoClient  ");
	  	return;	
	  }
	  
	  System.out.println("connect to the server.["+
	    args[0]+", "+args[1]+"]");
	  int port = Integer.parseInt(args[1]);
	  
	  SSLContext ctx;
	  KeyManagerFactory kmf;
	  KeyStore ks;
	  char[] passphrase = "java11".toCharArray();
      
	  ctx = SSLContext.getInstance("TLS");
	  kmf = KeyManagerFactory.getInstance("SunX509");
	  ks = KeyStore.getInstance("JKS");
      
	  ks.load(new FileInputStream("config/jnet.keystore"), passphrase);
      
	  kmf.init(ks, passphrase);
	  ctx.init(kmf.getKeyManagers(), null, null);
      
	  SSLSocketFactory factory = ctx.getSocketFactory();
      SSLSocket sck = (SSLSocket)factory.createSocket(args[0], port);
	  sck.setEnabledCipherSuites(sck.getSupportedCipherSuites());
	  sck.startHandshake();
	  
	  BufferedInputStream in = 
	  	new BufferedInputStream(sck.getInputStream());
	  BufferedOutputStream out = 
	  	new BufferedOutputStream(sck.getOutputStream());
	  
	  String msg = "hongseong";
	  byte[] tmp = msg.getBytes();
	  
	  out.write(tmp, 0, tmp.length);
	  out.flush();
	  
	  byte[] buff = new byte[1024];
	  int len = in.read(buff);
	  
	  System.out.println("received message>> "+new String(buff, 0, len));
	  out.close();
	  in.close();
	  sck.close();	

  }
}
 

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 가 처리한다는 것을 설정한 것이다.
	
    
<Connector name="con7131" className="jlook.jnet.connector.StandardConnector" 
	    	port="7131" 		ssl="true"
	        enableLookups="true" acceptCount="50"
	        minHandlers="5" 		maxHandlers="50">
	<InputAdapter 	source="everyone" 		
		connectionTimeout="10000" 	
		sendBufferSize="10240"
		receiveBufferSize="10240"
	  	className="jlook.jnet.connector.StandardInputAdapter">
		
		<Task 	name="echo2" initSize="5" maxSize="50"
		  className="jlook.jnet.connector.StandardTask"
		  beanClass="jlook.jnet.task.SSLEchoTask"/>
				
	</InputAdapter>		
	
	<InputAdapter 	source="192.168.0.13" 		
	   connectionTimeout="10000" 	
	   sendBufferSize="10240"
	   receiveBufferSize="10240"
  	   className="jlook.jnet.connector.StandardInputAdapter">  
		
	   <Task 	name="proxy" initSize="5" maxSize="50"
	     className="jlook.jnet.connector.StandardTask"
	     beanClass="jlook.jnet.task.SSLProxyTask">
	     <Parameter name="target.server" value="192.168.0.13"/>  
	     <Parameter name="target.port"	 value="7130"/>
	     <Parameter name="buffer.size"	 value="9"/>
	   </Task>
						
	</InputAdapter>
◆ 이제 SSLProxyTask Beans의 소스를 살펴 볼것인데, 위에서 Task의 Parameter 태그는 Task Beans에 넘길 key/value 데이터를 설정한 태그이다. 포워딩할 서버 및 포트 정보를 설정했다. 이것을 어떻게 얻어 내었는지 살펴보기 바란다.
 
package jlook.jnet.task;

import java.io.*;
import java.net.*;

import jlook.jnet.Keys;
import jlook.jnet.util.Logger;

public class SSLProxyTask extends NetTaskSupport {
	private static Logger logger = Logger.getLogger(Keys.LOGGER);
	
	private String 	server;
	private int 	port;
	private int 	bufferSize;
	
	public static final String TARGET_SERVER 	= "target.server";
	public static final String TARGET_PORT 	= "target.port";
	public static final String BUFFER_SIZE 	= "buffer.size";
	
	public void taskCreate() {
		logger.debug("SSLProxyTask#taskCreate()- "+id);	
	}
	
	public void taskActivate(){
		logger.debug("SSLProxyTask#taskActivate()- "+id);	
	}
	
	public void taskPassivate(){
		logger.debug("SSLProxyTask#taskPassivate()- "+id);	
	}
	
	public void taskDestroy(){
		logger.debug("SSLProxyTask#taskDestroy()- "+id);	
	}
	
	public void setContext(NetContext context) {
		super.setContext(context);
		logger.debug("SSLProxyTask#setContext()- "+id);	
		server = context.getParameter(TARGET_SERVER);
		if(server == null) server = "127.0.0.1";
		try {
		  port = Integer.parseInt(context.getParameter(TARGET_PORT));
		} catch(Exception e) {
			port=80;
		}
		try {
		  bufferSize = Integer.parseInt(
		  		context.getParameter(BUFFER_SIZE));
		} catch(Exception e) {
			bufferSize=10240;
		}
	}
	
	public void doTask(BufferedInputStream in, BufferedOutputStream out) 
	throws TaskException {
		Logger logger = context.getLogger();
		logger.debug("SSLProxyTask#doTask()- "+id);	
		
		Socket proxy = null;
		BufferedInputStream 	pin  = null;
		BufferedOutputStream 	pout = null;
		try {
		  proxy= new Socket(server,port);
		  pin  = new BufferedInputStream(proxy.getInputStream());
		  pout = new BufferedOutputStream(proxy.getOutputStream());
		} catch(Exception e) {
		  String msg = "Cannot connect to target system>> "+
			e.getMessage();
		  logger.error(msg, e);
		  byte[] tmp = msg.getBytes();
		  try {
			out.write(tmp, 0, tmp.length);
			out.flush();
	  	  } catch(IOException ex){}
		  return;
		}
		
		try {
			byte[] buff = new byte[bufferSize];
			while(true) {
				int len = in.read(buff);
				if(len>"+id;	
	}
}
◆ 다음은 runssl.bat 파일의 실행 결과이다. 서버 콘솔을 보면 클라이언트 요청이 7131{SSLProxyTask}, 7130{EchoTask} 두번 처리된것을 확인할 수 있다. ### Client console] C:\jNetServer1.0>runssl CLASSPATH=.;D:\bea\weblogic81\server\lib\weblogic.jar;server\lib\jNetServer1.0.j ar;common\classes;config connect to the server.[192.168.0.13, 7131] received message>> Hello hongseong ### Server console] [01:54:29] DEBUG - @ request client address : 192.168.0.13 [01:54:29] DEBUG - StandardInputAdapter#doExecute() is started [01:54:29] DEBUG - InputAdapter>>svr1/con7131/192.168.0.13 [01:54:29] DEBUG - StandardInputAdapter#doExecute()- start handshake [01:54:29] DEBUG - SSLProxyTask#taskActivate()- SSLProxyTask{proxy}[4] [01:54:29] DEBUG - SSLProxyTask#setContext()- SSLProxyTask{proxy}[4] [01:54:29] DEBUG - SSLProxyTask#doTask()- SSLProxyTask{proxy}[4] [01:54:29] DEBUG - @ request client address : 192.168.0.13 [01:54:29] DEBUG - StandardInputAdapter#doExecute() is started [01:54:29] DEBUG - InputAdapter>>svr1/con7130/everyone [01:54:29] DEBUG - EchoTask#taskActivate()- EchoTask{echo1}[2] [01:54:29] DEBUG - EchoTask#setContext()- EchoTask{echo1}[2] [01:54:29] DEBUG - EchoTask#doTask()- EchoTask{echo1}[2] [01:54:29] DEBUG - EchoTask{echo1}[2]#doTask()- read.... [01:54:29] DEBUG - EchoTask{echo1}[2]#doTask()- received msg>> hongseong [01:54:29] DEBUG - EchoTask#doEnd()- EchoTask{echo1}[2] [01:54:29] DEBUG - EchoTask#taskPassivate()- EchoTask{echo1}[2] [01:54:29] DEBUG - SSLProxyTask#doEnd()- SSLProxyTask{proxy}[4] [01:54:29] DEBUG - SSLProxyTask#taskPassivate()- SSLProxyTask{proxy}[4] [01:54:29] DEBUG - Handler{7130}[4] process time : 0.01 sec [01:54:29] DEBUG - Handler{7131}[4] process time : 0.37 sec

 
 written by Jeon HongSeong [hsjeon70@dreamwiz.com]      
 
1
References
 
This Article Source : jNetServer1.0-app.jar
Copyright ⓒ 2003 www.javapattern.info & www.jlook.com, an jLOOK co.,LTD