|
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 |