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


Lightweight Web Server Implementation
 
전에 쫀이 만들었던 HTTP Webserver만들기가 버겨웠다면 간단명료하게 웹서버의 기능을 갖는 네트웍프로그램을 만들어보자. ( 2003/03/18 ) 281
Written by ienvyou - 최지웅
1 of 1
 

일전에 올렸던 쫀~(전홍성님)이 쓴 HTTP Webserver만들기 편에서 모두들 이해를 잘했는지가
상당히 궁금하다. 이번 편에서는 아주 간단한 초미니 웹서버를 만드는 방법을 
해보겠는데 소켓과 스레드에 대한 내용을 조금만 알고 있고, http protocol의 브라우져가
식별할 수 있는 헤더메시지를 안다면 쉽게 만들수 있을 것이다.

▶ 미니웹서버 만들기

자. 웹서버의 기본작동원리는 무엇일까? 자바로 웹서버를 만든다면 당연히 스레드를 이용할
것이며, c로서 만들게 된다면 각 요청에 대한 process를 fork시켜 서비스핸들러를 만들어처리를
하게 될 것이란게 머리속에 조금은 들어오는가?

자바로 소켓프로그램을 짜보았다면 순서는 어떻게 되는 것인가? 

1. 서버데몬을 구동한다.
2. 클라이언트가 서버측의 서버소켓으로 접근을 시도한다.
3. 서버는 클라이언트와 연결되어질 소켓객체를 생성한다.

자, 이제 4번부터가 P2P방식인지 server daemon방식의 서비스인지가 결정이 날수 있는데
스레드를 생성하지 않고서 직접 클라이언트의 request를 처리한다고 가정하자.

만약 서비스를 완료하는데 걸리는 시간이 1분이었다고 가정을 하게 된다면 다음 요청을 하게되는
클라이언트는 어떻게 될것인가? 

connection timeout 또는 connection refused를 경험하게 될것이다. 궁금한가? 직접 짜보라~!
아래와 같은 코드를 이용하여 테스트해보면 될것이다.



▶ Server측
    public MiniWebServer() {
        try
        {
            ServerSocket me = new ServerSocket( 8081 );
            Socket client = me.accept();
            Thread.sleep(1000 * 60);
			System.out.println( "client: " + client.getInetAddress() );
        }
        catch ( Exception  ex )	{
                System.out.println( ex + "server start failed!!" );
        }
    }

▶Client측
    public void connect() {
        Socket soc = new Socket("127.0.0.1", 8081);
    }


간단하게 위처럼만 짜보고 테스트해보라. 서버는 클라이언트의 요청이 들어오게 되면
1분동안을 바보처럼 가만히 잠자게 될때 다른 클라이언트로 접근을 시도해보라~

결과는 여러분들이 직접 짜보도록 한다.

각설하고 계속 나가겠다. 그러면 위와 같은 서비스의 끊김현상이 발생하지 않고, 클라이언트의
요청을 족족이 모두 서비스가 가능하게 하려면 어떻게 해야 하나?

답은 바로 위에도 이야기 했던것처럼 스레드이다.

4. 클라이언트의 요청이 들어오면 해당 소켓을 서비스핸들링을 할 수 있는 스레드로 전이시킨다.
5. 소켓을 넘겨넣은 스레드는 실제 요청이 들어온 클라이언트에게 서비스를 하도록 한다.
6. 요청을 모두 처리했으면 http속성답게 연결을 끊어버린다.


위의 시나리오가 전부이다.

네트웍프로그램의 불변의 법칙을 아는가? 아주 간단하다.
간단한 만큼 네트웍프로그래밍 방법을 간단하게 설명하겠다.

1. 서버측프로그램을 작성한다.
    - 서버소켓을 생성한다.
    - 무한루프를 이용하여 서버소켓의 accept()로 클라이언트요청을 기다린다.
    - 클라이언트요청이 들어올 경우 서비스를 할 스레드를 생성하고 accept()에 의하여 넘겨진 
    socket을 스레드측으로 전달한다.
    - 서버를 다시 서비스를 계속한다.

2. 클라이언트측 프로그램을 작성한다.
    - 서버측으로 접속을 시도할 수 있는 소켓을 IP와 포트로 생성한다.
    - 보내고자 하는 데이터를 stream으로 필터링하여 서버로 전송한다.

이런~~ 쓸려니 조금 힘들다. 네트웍프로그래밍을 자유자재로 하고 싶은가? 그렇다면 놀새의 이야기를
들어주길 바란다. 

Thread개념, IO개념만 정확하게 안다면 네트웍프로그램은 51% 정복한 거나 다름 없다.

그만 떠들고 프로그램이나 보이라는 소리가 여기저기서 들리는듯하다. 아흐~

자. 프로그램으로 들어간다. 먼저 서버소켓을 이용하여 클라이언트의 요청을 받을 수 있는 
서버프로그램을 스레드형태로 작성해보자. 

▶ 서버측 데몬

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

public class  MiniWebServer extends Thread {
    private ServerSocket me;

    public MiniWebServer() {
        try
        {
            me = new ServerSocket( 9090 );
        }
        catch ( IOException  ex )	{
                System.out.println( ex + "server start failed!!" );
        }
    }

    public void run() {
        while ( true )	{
            try	{
                Socket client = me.accept();
                System.out.println( "client: " + client.getInetAddress() );
                new ServiceHandler( client );
            }catch ( Exception ex ){
                System.out.println( ex );
            }
        }
    }

    public static void main(String[] args) 	{
        MiniWebServer mini = new MiniWebServer();
        mini.start();
        System.out.println("web server start!!");
    }
}

서버측 서비스 스레드

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

class ServiceHandler implements Runnable {
    private Socket client;

    public ServiceHandler( Socket c ) {
        try{
            client = c;
            System.out.println( client.getPort() + " : " + client.getLocalPort() );
            Thread t = new Thread( this );
            t.start();
        }catch(Exception e) {

        }
    }
    public void run()  {
        try	{
            PrintWriter out = new PrintWriter( client.getOutputStream() );
            out.print( "HTTP/1.0 OK\r\n" );
            out.print( "Server: HTTPServer 1.0\r\n" );
            out.print( "Content-Type: text/html\r\n\r\n" );
            out.println( "반갑습니다....<hr>" );
            out.println( "<br>놀새가 만든 세상... ^^" );
            out.close();
        }catch ( IOException ex ){
            System.out.println( ex );
        }
    }
}


아. 위의 프로그램이 조금이라도 이해가 가는가? 그렇다면 이 글을 읽는 당신~!!! 떠나라~~~!!

다른 아티클로 떠나라. 당장 여기를 누르기 바란다. 요기

자. 위에서 보게 되면 서버측의 accept()메소드가 수행되게 되면 바로 서비스 핸들러측으로
서버소켓이 넘겨준 Socket객체를 스레드로 넘겨주는 역할을 하게 되고 실제 그 서비스스레드는
PrintWriter객체를 클라이언트측에 갖다댄후 HTTP 프로토콜로 쏴주는 형식을 취하고 있다.

여러분들 서블릿을 작성해보았는가? 보았다면 reponse.getWriter()를 보았는가? 
기본적인 서블릿의 getWriter()를 하게 되면 이미 response의 스트림위에 저러한 http protocol의
헤더가 들어있는 것이다. 저 정보는 누가 해석을 하게 될까? 그렇다..바로 브라우져이다.

실행을 해보자. 설마 실행하는 방법을 모르는 JPer들이 있을까봐 놀새가 친절하게 적고 
그 결과까지 적어보도록 하겠다.

먼저 컴파일.

javac *java

자, 컴파일이 되었다면 서버데몬을 구동하도록 하자.

java MiniWebServer

자. 서버가 구동되어서 도스창이 먹통이 된다고 놀라지 않아야 한다.
"어라? 클라이언트 프로그램이 없는데 저걸 어떻게 테스트해"라고 물어보면 가슴이 저려올것이다.

클라이언트 프로그램은 무엇일까? 바로바로 웹브라우져이다.
웹브라우져를 바로 하나 띄워보도록 한다.

URL입력창에 아래와 같이 입력하도록 한다.

http://127.0.0.1:9090/

결과값이 아래와 같이 나온다면 대성공이다.

도스창
web server start!!
client: /127.0.0.1
3397 : 9090

웹브라우져



위와 같이 나오는가?

신기한가? 잘 분석해보길 바라며, 위의 서비스 핸들러를 이용하여 조작하게 된다면 여러분도
전에 쫀이 만들 웹서버같은 간단한 초경량 웹서버? 문제도 아니다.

건투를 빈다.
 
1
References
 
Copyright ⓒ 2003 www.javapattern.info & www.jlook.com, an jLOOK co.,LTD