레이블이 java인 게시물을 표시합니다. 모든 게시물 표시
레이블이 java인 게시물을 표시합니다. 모든 게시물 표시

2012년 3월 13일 화요일

Java에서 signal 처리 - JNI -

출처 : http://www.javaservice.net/~java/bbs/read.cgi?m=devtip&b=javatip&c=r_p&n=978538886

Java에서 signal 처리 - JNI -



signal 핸들링은 (제 생각엔) Java 에선 뜨거운 감자 같습니다. 왜냐면, signal
핸들링은 OS 마다 다른데, 어떤 OS에서든 동작해야 하는 Java 입장에선 일반화된
Java API를 제공하기엔 바람직하지 않은 듯 합니다.

예를 들어 kill 명령어는 Unix 에서는 일반적이지만, Windows 에선 (제가 알기로는)
kill 명령어가 없듯이 (즉, signal 를 해당 process 에게 줄수 있는 방법이 없듯이)
java 에서 signal 핸들링은 결국 OS 종속적인 JNI 를 이용해 필요할 때 알아서
사용토록하고, Java 는 관여치 않는 듯 합니다.

C 에서 signal 핸들링을 해 보셨고, 또, Java 의 JNI 를 해 보셨다면, 저처럼
어렵지 않게 다음과 같은 테스트를 해 볼 수 있을 겁니다.


첫번째 예제는 실행된 Java 프로그램에서 가능한 모든 종류의 signal 을 잡아보는 
예제입니다. 기본개념은 JNI C 메소드를 호출해 signal 을 등록하고, C native 코드에서
signal 이 오면, Java 의 특정 메소드를 JNI callback 을 이용해 호출시켜 주는
구조입니다.


javaservice:/home/guest/source/signal$ ls -alF
total 12
drwxr-xr-x   3 java     java         4096 Jan  1 23:21 ./
drwxrwxrwx   6 guest    users        4096 Jan  1 23:21 ../
drwxr-xr-x   3 java     java         4096 Jan  1 23:20 org/
javaservice:/home/guest/source/signal$ ls -alF org/jsn/signal/
total 12
drwxr-xr-x   2 java     java         4096 Jan  1 23:36 ./
drwxr-xr-x   3 java     java         4096 Jan  1 23:21 ../
-rw-r--r--   1 java     java          651 Jan  1 23:36 SignalTest.java
javaservice:/home/guest/source/signal# cat org/jsn/signal/SignalTest.java
package org.jsn.signal;

public class SignalTest {

  public SignalTest() {
    for(int i=1;i<31;i++)
        signal(i, "handle");
    System.out.println("signal listening");
  }

  public void do_something(){
    // infinite loop
    for(int i=0; true ; i++) {
      System.out.println("waiting..." + i);
      try{Thread.sleep(1000 * 10);}catch(Exception e){}
    }
  }

  public native synchronized void signal(int signum, String handles);


 /* This callback method will be called by native code*/
  public void handle(int signum) {
    System.out.println("I have got signal : " + signum);
    if ( signum == 3 || signum == 9 || signum == 15 ) {
      System.out.println("gracefully exited, really?");
      System.exit(0);// NOTE: this does NOT work;
    }
  }

  static {
    try{
      System.loadLibrary("signalhandle");
    }catch(Exception e){
      e.printStackTrace();
    }
  }
  public static void main(String[] args){
    SignalTest test = new SignalTest();
    test.do_something();
  }
}


javaservice:/home/guest/source/signal$
javaservice:/home/guest/source/signal$ javac org/jsn/signal/SignalTest.java
javaservice:/home/guest/source/signal$ ls -alF org/jsn/signal/
total 16
drwxr-xr-x   2 java     java         4096 Jan  1 23:37 ./
drwxr-xr-x   3 java     java         4096 Jan  1 23:21 ../
-rw-r--r--   1 java     java         1088 Jan  1 23:37 SignalTest.class
-rw-r--r--   1 java     java          651 Jan  1 23:36 SignalTest.java
javaservice:/home/guest/source/signal$ javah -jni org.jsn.signal.SignalTest
javaservice:/home/guest/source/signal$ ls -alF
total 16
drwxr-xr-x   3 java     java         4096 Jan  1 23:38 ./
drwxrwxrwx   6 guest    users        4096 Jan  1 23:21 ../
drwxr-xr-x   3 java     java         4096 Jan  1 23:20 org/
-rw-r--r--   1 java     java          488 Jan  1 23:38 org_jsn_signal_SignalTest.h
javaservice:/home/guest/source/signal# cat org_jsn_signal_SignalTest.h
/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class org_jsn_signal_SignalTest */

#ifndef _Included_org_jsn_signal_SignalTest
#define _Included_org_jsn_signal_SignalTest
#ifdef __cplusplus
extern "C" {
#endif
/*
 * Class:     org_jsn_signal_SignalTest
 * Method:    signal
 * Signature: (ILjava/lang/String;)V
 */
JNIEXPORT void JNICALL Java_org_jsn_signal_SignalTest_signal
  (JNIEnv *, jobject, jint, jstring);

#ifdef __cplusplus
}
#endif
#endif
javaservice:/home/guest/source/signal#

(....... vi signalhandle.c .......   )

javaservice:/home/guest/source/signal# ls -alF
total 48
   4 drwxr-xr-x   3 java     java         4096 Jan  2 01:38 ./
   4 drwxrwxrwx   6 guest    users        4096 Jan  2 01:33 ../
   8 -rw-r--r--   1 root     root         4625 Jan  2 01:28 NativeStringUtil.c
   4 -rw-r--r--   1 root     root          559 Jan  2 01:28 NativeStringUtil.h
   4 -rw-r--r--   1 root     root          129 Jan  2 01:38 cc
   4 drwxr-xr-x   3 java     java         4096 Jan  1 23:20 org/
   4 -rw-r--r--   1 java     java          488 Jan  2 01:35 org_jsn_signal_SignalTest.h
   4 -rw-r--r--   1 java     java          829 Jan  2 01:37 signalhandle.c
javaservice:/home/guest/source/signal#
javaservice:/home/guest/source/signal# cat signalhandle.c
#include <signal.h>
#include "org_jsn_signal_SignalTest.h"
#include "NativeStringUtil.h"

/*
 NOTE: global variable !!
 */
JNIEnv *g_env;
jobject g_obj;
jmethodID g_mid;

void signal_handle(signum)
int signum;
{
   if ( g_mid == 0 ) return;
   (*g_env)->CallVoidMethod(g_env, g_obj, g_mid, signum);
}

/*
 * Class:     org_jsn_signal_SignalTest
 * Method:    signal
 * Signature: (ILjava/lang/String;)V
 */
JNIEXPORT void JNICALL Java_org_jsn_signal_SignalTest_signal
  (JNIEnv *env, jobject obj, jint signum, jstring handle_name)
{
  jclass cls = (*env)->GetObjectClass(env, obj);
  char *method = jbyteArray2cstr( env, javaGetBytes(env, handle_name) );
  jmethodID mid = (*env)->GetMethodID(env,cls, method, "(I)V");
  if ( mid == 0 ) return;
  g_env = env;
  g_obj = obj;
  g_mid = mid;

  signal(signum, signal_handle);
}


javaservice:/home/guest/source/signal# cat cc
#!/bin/sh
gcc -shared -I/usr/java/include \
-I/usr/java/include/linux \
-o libsignalhandle.so signalhandle.c NativeStringUtil.c

javaservice:/home/guest/source/signal# sh cc
javaservice:/home/guest/source/signal#
javaservice:/home/guest/source/signal# export LD_LIBRARY_PATH=.:$LD_LIBRARY_PATH
javaservice:/home/guest/source/signal# export CLASSPATH=.:$CLASSPATH
javaservice:/home/guest/source/signal#
javaservice:/home/guest/source/signal# java org.jsn.signal.SignalTest
signal listening
waiting...0
waiting...1
waiting...2
....

이렇게 수행되고 있을 때, 다른 터미널에서 kill 명령어를 날려 봅니다.

[Terminal B]
javaservice:/root# ps -ef|grep java|grep root
root     17102 15065  2 01:00 pts/4    00:00:00 /usr/java/bin/linux/native_threa
root     17110 17102  0 01:00 pts/4    00:00:00 /usr/java/bin/linux/native_threa
root     17111 17110  0 01:00 pts/4    00:00:00 /usr/java/bin/linux/native_threa
root     17112 17110  0 01:00 pts/4    00:00:00 /usr/java/bin/linux/native_threa
root     17115 15768  0 01:00 pts/3    00:00:00 grep java
javaservice:/root# kill -2 17102
javaservice:/root# kill -3 17102
javaservice:/root# kill -15 17102
javaservice:/root# kill -10 17102
javaservice:/root# kill -9 17102
javaservice:/root#


결과는 다음과 같습니다.

javaservice:/home/guest/source/signal# java org.jsn.signal.SignalTest
signal listening
waiting...0
waiting...1
waiting...2
I have got signal : 2
I have got signal : 3
gracefully exited, really?
waiting...3
I have got signal : 15
gracefully exited, really?
waiting...4
I have got signal : 10
waiting...5
Killed
javaservice:/home/guest/source/signal#

이 프로그램은 Ctl-C 를 눌려도 종료되지 않습니다. signal 2 : SIGINT 가 잡히기
때문입니다. 오로지 다른 터미널 창에서 kill -9 를 날려야만 죽습니다.
주의할 점은 JNI의 callback 을 통해 수행되는 Java code 에서 System.exit() 을
날려도 JVM 이 exit 되지는 않습니다.

NOTE: signal 번호는 아래의 첨부 A 를 참조하세요.

따라서, 이와 같은 signal 처리는 단지 자바프로그램에서 process 의 signal 을
받아 무언가를 하도록 할 때 사용될 수 있을 뿐이며, "gracefully exiting" 을
이처럼 JNI의 callback 을 이용해 구현할 수는 없는 듯 합니다. 즉, Ctl-C 를
누른다거나, kill -9 를 제외한 kill -3 (SIGQUIT), kill -15(SIGTERM) 를 잡아채어
미진한 종료작업을 수행한 수 JVM이 내려가도록 하려면 뭔가 다른 방법을 강구해야
하는 듯 합니다.


JNI callback 을 사용하지 말아야 한다면, JNI 메소드를 부르되 signal 이
올 때까지 blocking 이 걸려있으면 될 듯 한데, 어디 한번 또 해 볼까요....

[두번째 예제] "gracefully exiting"
기본적인 개념은 별도의 자바 Thread 가 C native 코드를 호출하여 특정 signal 이
발생할 때 까지 기다리게 됩니다.


javaservice:/home/guest/source/signal2#
javaservice:/home/guest/source/signal2#
javaservice:/home/guest/source/signal2# ls -alF
total 24
   4 drwxr-xr-x   3 root     root         4096 Jan  2 02:30 ./
   4 drwxrwxrwx   7 guest    users        4096 Jan  2 01:43 ../
   4 -rw-r--r--   1 root     root          129 Jan  2 01:43 cc
   4 drwxr-xr-x   3 root     root         4096 Jan  2 01:43 org/
   4 -rw-r--r--   1 root     root          269 Jan  2 02:26 signalhandle2.c
   
javaservice:/home/guest/source/signal2#
javaservice:/home/guest/source/signal2#
javaservice:/home/guest/source/signal2# ls -alF org/jsn/signal/
total 24
   4 drwxr-xr-x   2 root     root         4096 Jan  2 02:03 ./
   4 drwxr-xr-x   3 root     root         4096 Jan  2 01:43 ../
   4 -rw-r--r--   1 root     root          488 Jan  2 02:03 SignalMonitor.java
   4 -rw-r--r--   1 root     root          505 Jan  2 02:00 SignalTest2.java

javaservice:/home/guest/source/signal2# cat org/jsn/signal/SignalMonitor.java
package org.jsn.signal;

public class SignalMonitor extends Thread
{
  static {
    try{
        System.loadLibrary("signalhandle2");
    }catch(Exception e){
        e.printStackTrace();
    }
  }
  public SignalMonitor(){
    super();
  }
  public void run(){
    signal_wait(15);  // SIGTERM
    System.out.println("I've got SIGTERM signal");
    System.out.println("program terminated gracefully");
    System.exit(1);
  }
  public native synchronized void signal_wait(int signum);
}

javaservice:/home/guest/source/signal2#
javaservice:/home/guest/source/signal2# cat org/jsn/signal/SignalTest2.java
package org.jsn.signal;

public class SignalTest2 {

  public SignalTest2() {
    SignalMonitor monitor = new SignalMonitor();
    monitor.start();
    System.out.println("signal listening");
  }

  public void do_something(){
    // infinite loop
    for(int i=0; true ; i++) {
      System.out.println("waiting..." + i);
      try{Thread.sleep(1000 * 10);}catch(Exception e){}
    }
  }

  public static void main(String[] args){
    SignalTest2 test = new SignalTest2();
    test.do_something();
  }
}

javaservice:/home/guest/source/signal2#
javaservice:/home/guest/source/signal2#
javaservice:/home/guest/source/signal2# javac org/jsn/signal/*.java
javaservice:/home/guest/source/signal2# ls -alF org/jsn/signal/
total 24
   4 drwxr-xr-x   2 root     root         4096 Jan  2 02:44 ./
   4 drwxr-xr-x   3 root     root         4096 Jan  2 01:43 ../
   4 -rw-r--r--   1 root     root          813 Jan  2 02:55 SignalMonitor.class
   4 -rw-r--r--   1 root     root          489 Jan  2 02:44 SignalMonitor.java
   4 -rw-r--r--   1 root     root          953 Jan  2 02:55 SignalTest2.class
   4 -rw-r--r--   1 root     root          505 Jan  2 02:00 SignalTest2.java
javaservice:/home/guest/source/signal2#
javaservice:/home/guest/source/signal2# javah -jni org.jsn.signal.SignalMonitor
javaservice:/home/guest/source/signal2# ls -alF
total 40
   4 drwxr-xr-x   3 root     root         4096 Jan  2 02:05 ./
   4 drwxrwxrwx   7 guest    users        4096 Jan  2 01:43 ../
   4 -rw-r--r--   1 root     root          129 Jan  2 01:43 cc
   4 drwxr-xr-x   3 root     root         4096 Jan  2 01:43 org/
   4 -rw-r--r--   1 root     root          487 Jan  2 02:05 org_jsn_signal_SignalMonitor.h
   4 -rw-r--r--   1 root     root          269 Jan  2 02:26 signalhandle2.c

javaservice:/home/guest/source/signal2#
javaservice:/home/guest/source/signal2# cat org_jsn_signal_SignalMonitor.h
/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class org_jsn_signal_SignalMonitor */

#ifndef _Included_org_jsn_signal_SignalMonitor
#define _Included_org_jsn_signal_SignalMonitor
#ifdef __cplusplus
extern "C" {
#endif
/*
 * Class:     org_jsn_signal_SignalMonitor
 * Method:    signal_wait
 * Signature: (I)V
 */
JNIEXPORT void JNICALL Java_org_jsn_signal_SignalMonitor_signal_1wait
  (JNIEnv *, jobject, jint);

#ifdef __cplusplus
}
#endif
#endif

javaservice:/home/guest/source/signal2#
javaservice:/home/guest/source/signal2# cat signalhandle2.c
#include <signal.h>
#include "org_jsn_signal_SignalMonitor.h"

int signal_reached = 0;

void signal_handle(signum)
int signum;
{
   signal_reached = 1;
}

/*
 * Class:     org_jsn_signal_SignalMonitor
 * Method:    signal_wait
 * Signature: (I)V
 */
JNIEXPORT void JNICALL Java_org_jsn_signal_SignalMonitor_signal_1wait
  (JNIEnv *env, jobject obj, jint signum)
{
   signal_reached = 0;
   signal(signum, signal_handle);
   while ( signal_reached == 0 )
       ;
   /* CPU를 다소 점유할텐데, 더 좋은 방법 있나요? */
}

javaservice:/home/guest/source/signal2#
javaservice:/home/guest/source/signal2# cat cc
#!/bin/sh

gcc -shared -I/usr/java/include \
-I/usr/java/include/linux \
-o libsignalhandle2.so signalhandle2.c


javaservice:/home/guest/source/signal2#
javaservice:/home/guest/source/signal2# sh cc
javaservice:/home/guest/source/signal2# ls -alF
total 32
   4 drwxr-xr-x   3 root     root         4096 Jan  2 02:36 ./
   4 drwxrwxrwx   7 guest    users        4096 Jan  2 01:43 ../
   4 -rw-r--r--   1 root     root          112 Jan  2 02:35 cc
   8 -rwxr-xr-x   1 root     root         5299 Jan  2 02:36 libsignalhandle2.so*
   4 drwxr-xr-x   3 root     root         4096 Jan  2 01:43 org/
   4 -rw-r--r--   1 root     root          482 Jan  2 02:29 org_jsn_signal_SignalMonitor.h
   4 -rw-r--r--   1 root     root          283 Jan  2 02:36 signalhandle2.c


이제 실행시켜 보겠습니다.

javaservice:/home/guest/source/signal2# java org.jsn.signal.SignalTest2
signal listening
waiting...0
waiting...1
......



실행되고 있는 중에 다른 Terminal 창에서 다음과 같이 SIGTERM(15) 을 날립니다.

[Terminal B]
javaservice:/root# ps -ef|grep java|grep root
root     19104 15065  4 02:50 pts/4    00:00:00 /usr/java/bin/linux/native_threa
root     19112 19104  0 02:50 pts/4    00:00:00 /usr/java/bin/linux/native_threa
root     19113 19112  0 02:50 pts/4    00:00:00 /usr/java/bin/linux/native_threa
root     19114 19112  0 02:50 pts/4    00:00:00 /usr/java/bin/linux/native_threa
root     19115 19112 99 02:50 pts/4    00:00:05 /usr/java/bin/linux/native_threa
root     19117 15768  0 02:50 pts/3    00:00:00 grep java
javaservice:/root# kill -15 19104
javaservice:/root#


결과는 다음과 같습니다.

javaservice:/home/guest/source/signal2# java org.jsn.signal.SignalTest2
signal listening
waiting...0
waiting...1
I've got SIGTERM signal
program terminated gracefully
javaservice:/home/guest/source/signal2#


원하시는 답변이 되었기를 바랍니다.




----------------------------------------------------------------------------------
2000.1.4 CPU 점거현상 보완 추가

CPU를 점거하는 문제를 해결하였습니다. 아래 문서를 참조하세요.

"CPU 점거현상 보완 추가"
http://www.javaservice.net/~java/bbs/read.cgi?m=devtip&b=javatip&c=r_p&n=978538886



[첨부A signal.h : Linux]

/* Signals.  */
#define SIGHUP      1   /* Hangup (POSIX).  */
#define SIGINT      2   /* Interrupt (ANSI).  */
#define SIGQUIT     3   /* Quit (POSIX).  */
#define SIGILL      4   /* Illegal instruction (ANSI).  */
#define SIGTRAP     5   /* Trace trap (POSIX).  */
#define SIGABRT     6   /* Abort (ANSI).  */
#define SIGIOT      6   /* IOT trap (4.2 BSD).  */
#define SIGBUS      7   /* BUS error (4.2 BSD).  */
#define SIGFPE      8   /* Floating-point exception (ANSI).  */
#define SIGKILL     9   /* Kill, unblockable (POSIX).  */
#define SIGUSR1     10  /* User-defined signal 1 (POSIX).  */
#define SIGSEGV     11  /* Segmentation violation (ANSI).  */
#define SIGUSR2     12  /* User-defined signal 2 (POSIX).  */
#define SIGPIPE     13  /* Broken pipe (POSIX).  */
#define SIGALRM     14  /* Alarm clock (POSIX).  */
#define SIGTERM     15  /* Termination (ANSI).  */
#define SIGSTKFLT   16  /* Stack fault.  */
#define SIGCLD      SIGCHLD /* Same as SIGCHLD (System V).  */
#define SIGCHLD     17  /* Child status has changed (POSIX).  */
#define SIGCONT     18  /* Continue (POSIX).  */
#define SIGSTOP     19  /* Stop, unblockable (POSIX).  */
#define SIGTSTP     20  /* Keyboard stop (POSIX).  */
#define SIGTTIN     21  /* Background read from tty (POSIX).  */
#define SIGTTOU     22  /* Background write to tty (POSIX).  */
#define SIGURG      23  /* Urgent condition on socket (4.2 BSD).  */
#define SIGXCPU     24  /* CPU limit exceeded (4.2 BSD).  */
#define SIGXFSZ     25  /* File size limit exceeded (4.2 BSD).  */
#define SIGVTALRM   26  /* Virtual alarm clock (4.2 BSD).  */
#define SIGPROF     27  /* Profiling alarm clock (4.2 BSD).  */
#define SIGWINCH    28  /* Window size change (4.3 BSD, Sun).  */
#define SIGPOLL     SIGIO   /* Pollable event occurred (System V).  */
#define SIGIO       29  /* I/O now possible (4.2 BSD).  */
#define SIGPWR      30  /* Power failure restart (System V).  */
#define SIGUNUSED   31


-------------------------------------------------------  
  본 문서는 자유롭게 배포/복사 할 수 있으나 반드시
  이 문서의 저자에 대한 언급을 삭제하시면 안됩니다
================================================
  자바서비스넷 이원영
  E-mail: javaservice@hanmail.net
  PCS:019-310-7324
================================================

제목 : Re: CPU 점거 보완
글쓴이: 까막(guest) 2001/01/02 09:56:52 조회수:1756 줄수:10
안녕하세요 첨뵙겠습니다 ^^
Splenit의 까막이라고 합니다.
while문에 의한 부하문제는 sleep()으로 재운다음에
raise(SIGARLM)으로 깨우면 될 듯 합니다.
즉 signal_reached = 1;
대신에 raise(SIGALRM)
while(signal_reached == 0) 이 아니라 sleep(0)
가 되겠죠 ^^
이러면 while문에의한 CPU점거문제는 해결될듯 합니다.
Java는 아니고 C로 데몬짤때 썼던 코드입니당~~
제목 : CPU 점거현상 보완 추가
글쓴이: 이원영(javaservice) 2001/01/04 01:21:26 조회수:3744 줄수:119
2000.1.4 CPU 점거현상 보완 추가

까막님(whitecrw@orgio.net)이 raise(SIGALRM)과 sleep() 을 이용해
보라고 하셨는데, 막상 해 보니 sleep() 이 완전한 blocking 에 빠지지 않고 대략
8초 후에 그냥 빠져나와 버리더군요... 테스트한 소스는 다음과 같습니다.

---------------------------------------
void signal_handle(signum)
int signum;
{
   raise(SIGALRM);  
}

NIEXPORT void JNICALL Java_org_jsn_signal_SignalMonitor_signal_1wait
  (JNIEnv *env, jobject obj, jint signum)
{
    signal(signum, signal_handle);
    sleep(0);
}
---------------------------------------



GNU의 signal 처리기법에 관련한 문서에 보면 그와 같은 문제가 있다고 지적되어
있으며, 다음과 같이 하라고 명시되어 있습니다.

Sample 1:
---------------------------------------
int signal_reached = 0;
void signal_handle(signum)
int signum;
{
   signal_reached = 1;
}

JNIEXPORT void JNICALL Java_org_jsn_signal_SignalMonitor_signal_1wait
  (JNIEnv *env, jobject obj, jint signum)
{
    signal_reached = 0;
    signal(signum, signal_handle);
    while( ! signal_reached )
       sleep(1);
}
---------------------------------------

이 예제는 제대로 잘 동작합니다. 그러나, 그 문서에는 이러한 프로그래밍 보다는
아래와 같은 방법을 사용하도록 권하고 있습니다.

Sample 2:

---------------------------------------
....
int signal_reached = 0;

void signal_handle(signum)
int signum;
{
   signal_reached = 1;
}

JNIEXPORT void JNICALL Java_org_jsn_signal_SignalMonitor_signal_1wait
  (JNIEnv *env, jobject obj, jint signum)
{
    sigset_t mask, oldmask;

    signal_reached = 0;
    signal(signum, signal_handle);

    sigemptyset (&mask);
    sigaddset (&mask, SIGUSR1);
    sigprocmask (SIG_BLOCK, &mask, &oldmask);
    while(!signal_reached)
       sigsuspend (&oldmask);
    sigprocmask (SIG_UNBLOCK, &mask, NULL);
}
---------------------------------------

이 방법이 답일 듯 합니다.


GNU 관련 문서는 아래에 있습니다.
http://www.gnu.org/manual/glibc-2.0.6/html_chapter/libc_toc.html
http://www.gnu.org/manual/glibc-2.0.6/html_chapter/libc_21.html#SEC381



NOTE: 이 경우, JDK THREADS_FLAG  가 native 로 되어 있을 경우는 kill 명령어를 
날릴 때 잘 찾아서 날려야 합니다. native thread  모드에서는 여러개의 프로세스들이
나타납니다. 예를 들면,

javaservice:/root# ps -ef|grep java|grep root
root     24430 23282  0 00:50 pts/3    00:00:00 /usr/java/bin/linux/native_threa
root     24438 24430  0 00:50 pts/3    00:00:00 /usr/java/bin/linux/native_threa
root     24439 24438  0 00:50 pts/3    00:00:00 /usr/java/bin/linux/native_threa
root     24440 24438  0 00:50 pts/3    00:00:00 /usr/java/bin/linux/native_threa
root     24441 24438  0 00:50 pts/3    00:00:00 /usr/java/bin/linux/native_threa
root     24467 23416  0 00:51 pts/4    00:00:00 grep java
javaservice:/root# kill -15 24441

처럼, 제일 마지막 프로세스가 JNI 관련한 thread 처럼 보이며, 해당 프로세스 ID 에
signal 을 날려야 되더군요...

반면 THREADS_FLAG=green 일 경우는 아래 처럼 프로세스가 하나이니 별 혼선이 없네요.

javaservice:/root# ps -ef|grep java|grep root
root     24565 23282  5 00:53 pts/3    00:00:00 /usr/java/bin/i686/green_threads
root     24582 23416  0 00:53 pts/4    00:00:00 grep java
javaservice:/root# kill -15 24565

-------------------------------------------------------  
  본 문서는 자유롭게 배포/복사 할 수 있으나 반드시
  이 문서의 저자에 대한 언급을 삭제하시면 안됩니다
================================================
  자바서비스넷 이원영
  E-mail: javaservice@hanmail.net
  PCS:019-310-7324
================================================

2012년 1월 5일 목요일

Writing a multithreaded Socket Server

출처 : http://www.javareference.com/jrexamples/viewexample.jsp?id=47


Writing a multithreaded Socket Server

This example demonstrates the creation of a multithreaded socket server application. The server is currently a simple Echo Server, which simply accepts what the client says and sends it back. The example demonstrates the basic infrastructure for a true multithreaded server application and can be directly enhanced to perform as any server application.
The EchoServer accepts multiple client connections and is capable of serving them simultaneously. It uses a new thread to service every new client, which allows it to accepts new clients even before all clients are serviced. The server also identifies each client connection with a unique ID, making it easy to identify clients if required.
The EchoServer only understands one command, “quit”. On receiving this command, it quits the thread servicing the client and closes the connections. Also worth noting are the various places where anetwork connection could fail. All conditions are handled gracefully by EchoServer, thus making sure that the server never leaves behind residual threads and that the client is not capable of crashing the server.

Example Code:
EchoServer.java : The Server application

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

public class EchoServer
{        
    ServerSocket m_ServerSocket;
    
    
    public EchoServer() 
    {
        try
        {
            // Create the server socket. 
            m_ServerSocket = new ServerSocket(12111);
        }
        catch(IOException ioe)
        {
            System.out.println("Could not create server socket at 12111. Quitting.");
            System.exit(-1);
        }
        
        System.out.println("Listening for clients on 12111...");
        
        // Successfully created Server Socket. Now wait for connections. 
        int id = 0;
        while(true)
        {                        
            try
            {
                // Accept incoming connections. 
                Socket clientSocket = m_ServerSocket.accept();
            
                // accept() will block until a client connects to the server. 
                // If execution reaches this point, then it means that a client 
                // socket has been accepted. 
                
                // For each client, we will start a service thread to 
                // service the client requests. This is to demonstrate a 
                // multithreaded server, although not required for such a 
                // trivial application. Starting a thread also lets our 
                // EchoServer accept multiple connections simultaneously. 
                
                // Start a service thread 
                
                ClientServiceThread cliThread = new ClientServiceThread(clientSocket, id++);
                cliThread.start();
            }
            catch(IOException ioe)
            {
                System.out.println("Exception encountered on accept. Ignoring. Stack Trace :");
                ioe.printStackTrace();
            }
        }
    }
    
    public static void main (String[] args)
    {
        new EchoServer();    
    }
    
    
    class ClientServiceThread extends Thread
    {
        Socket m_clientSocket;        
        int m_clientID = -1;
        boolean m_bRunThread = true;
        
        ClientServiceThread(Socket s, int clientID)
        {
            m_clientSocket = s;
            m_clientID = clientID;
        }
       
        public void run()
        {            
            // Obtain the input stream and the output stream for the socket 
            // A good practice is to encapsulate them with a BufferedReader 
            // and a PrintWriter as shown below. 
            BufferedReader in = null; 
            PrintWriter out = null;
            
            // Print out details of this connection 
            System.out.println("Accepted Client : ID - " + m_clientID + " : Address - " + 
                             m_clientSocket.getInetAddress().getHostName());
                
            try
            {                                
                in = new BufferedReader(new InputStreamReader(m_clientSocket.getInputStream()));
                out = new PrintWriter(new OutputStreamWriter(m_clientSocket.getOutputStream()));
                
                // At this point, we can read for input and reply with appropriate output. 
                
                // Run in a loop until m_bRunThread is set to false 
                while(m_bRunThread)
                {                    
                    // read incoming stream 
                    String clientCommand = in.readLine();
                    
                    System.out.println("Client Says :" + clientCommand);
                    
                    
                    if(clientCommand.equalsIgnoreCase("quit"))
                    {
                        // Special command. Quit this thread 
                        m_bRunThread = false;   
                        System.out.print("Stopping client thread for client : " + m_clientID);
                    }
                    else
                    {
                        // Echo it back to the client. 
                        out.println(clientCommand);
                        out.flush();
                    }
                }
            }
            catch(Exception e)
            {
                e.printStackTrace();
            }
            finally
            {
                // Clean up 
                try
                {                    
                    in.close();
                    out.close();
                    m_clientSocket.close();
                    System.out.println("...Stopped");
                }
                catch(IOException ioe)
                {
                    ioe.printStackTrace();
                }
            }
        }
    }
}

EchoClient.java : The client application

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

// A client for our multithreaded EchoServer. 
public class EchoClient
{
    public static void main(String[] args)
    {
        // First parameter has to be machine name 
        if(args.length == 0)
        {
            System.out.println("Usage : EchoClient <serverName>");
            return;
        }
        
        Socket s = null;
        
        // Create the socket connection to the EchoServer. 
        try
        {
            s = new Socket(args[0], 12111);
        }        
        catch(UnknownHostException uhe)
        {
            // Host unreachable 
            System.out.println("Unknown Host :" + args[0]);
            s = null;
        }
        catch(IOException ioe)
        {
            // Cannot connect to port on given host 
            System.out.println("Cant connect to server at 12111. Make sure it is running.");
            s = null;
        }
        
        if(s == null)
            System.exit(-1);
        
        BufferedReader in = null;
        PrintWriter out = null;
        
        try
        {
            // Create the streams to send and receive information 
            in = new BufferedReader(new InputStreamReader(s.getInputStream()));
            out = new PrintWriter(new OutputStreamWriter(s.getOutputStream()));
            
            // Since this is the client, we will initiate the talking. 
            // Send a string. 
            out.println("Hello");
            out.flush();
            // receive the reply. 
            System.out.println("Server Says : " + in.readLine());
            
            // Send a string. 
            out.println("This");
            out.flush();
            // receive a reply. 
            System.out.println("Server Says : " + in.readLine());
            
            // Send a string. 
            out.println("is");
            out.flush();
            // receive a reply. 
            System.out.println("Server Says : " + in.readLine());
            
            // Send a string. 
            out.println("a");
            out.flush();
            // receive a reply. 
            System.out.println("Server Says : " + in.readLine());

            // Send a string. 
            out.println("Test");
            out.flush();
            // receive a reply. 
            System.out.println("Server Says : " + in.readLine());
            
            // Send the special string to tell server to quit. 
            out.println("Quit");
            out.flush();
        }
        catch(IOException ioe)
        {
            System.out.println("Exception during communication. Server probably closed connection.");
        }
        finally
        {
            try
            {
                // Close the streams 
                out.close();
                in.close();
                // Close the socket before quitting 
                s.close();
            }
            catch(Exception e)
            {
                e.printStackTrace();
            }                
        }        
    }
}

Demonstration:
To run the server, compile the source and run it by typing "java EchoServer" on the command prompt.
>java EchoServer
Listening for clients on 12111...

The server indicates that it is listening for connections. To run the client, simply run java EchoClient localhost (if running on the same machine), substitute "localhost" with the machine name the server is running on, if over a network.

>java EchoClient localhost
Server Says : Hello
Server Says : This
Server Says : is
Server Says : a
Server Says : Test

On running the client, the above output will be displayed at the client side, and the following output, on the server side.

Accepted Client : ID - 0 : Address - 127.0.0.1
Client Says :Hello
Client Says :This
Client Says :is
Client Says :a
Client Says :Test
Client Says :Quit
Stopping client thread for client : 0...Stopped