Category Archives: JAVA

[Java] 간단한 멀티쓰레드 웹서버 구현하기

학교 과제로 냈었던-_-a Java로 구현한 멀티쓰레드 웹서버입니다. Java로 구현할 수 있는 가장 기본적인 형태의 소켓 프로그래밍을 이용하여 구현하였습니다. 그냥 요청 받으면 해당 파일을 읽어서 내용을 보내는 단순한 어플리케이션입니다. Thread safe한 설계는 하지 않았습니다. 그냥 공부용으로만 사용해주세요.

* WebServer.java

class WebServer
{
    public static void main(String argv[]) throws Exception
    {
        // 서버소켓을 생성한다. 웹서버는 기본적으로 80번 포트를 사용한다.
        ServerSocket listenSocket = new ServerSocket(80);
        System.out.println("WebServer Socket Created");

        Socket connectionSocket;
        ServerThread serverThread;

        // 순환을 돌면서 클라이언트의 접속을 받는다.
        // accept()는 Blocking 메서드이다.
        while((connectionSocket = listenSocket.accept()) != null)
        {
            // 서버 쓰레드를 생성하여 실행한다.
            serverThread = new ServerThread(connectionSocket);
            serverThread.start();
        }
    }
} 

* ServerThread.java

public class ServerThread extends Thread
{
  // 파일 요청이 없을 경우의 기본 파일
  private static final String DEFAULT_FILE_PATH = "index.html";

  // 클라이언트와의 접속 소켓
  private Socket connectionSocket;

  /**
   * <pre>
   * 기본 생성자
   * </pre>
   * 
   * @param connectionSocket 클라이언트와의 통신을 위한 소켓
   */
  public ServerThread(Socket connectionSocket)
  {
    this.connectionSocket = connectionSocket;
  }

  /* (non-Javadoc)
   * @see java.lang.Thread#run()
   */
  @Override
  public void run()
  {
    System.out.println("WebServer Thread Created");
    BufferedReader inFromClient = null;
    DataOutputStream outToClient = null;

    try
    {
      // 클라이언트와 통신을 위한 입/출력 2개의 스트림을 생성한다.
      inFromClient = new BufferedReader(

            new InputStreamReader(connectionSocket.getInputStream()));
      outToClient = new DataOutputStream(
            connectionSocket.getOutputStream());

      // 클라이언트로의 메시지중 첫번째 줄을 읽어들인다.
      String requestMessageLine = inFromClient.readLine();

      // 파싱을 위한 토큰을 생성한다.
      StringTokenizer tokenizedLine = new StringTokenizer(
            requestMessageLine);

      // 첫번째 토큰이 GET으로 시작하는가? ex) GET /green.jpg
      if(tokenizedLine.nextToken().equals("GET"))
      {
        // 다음의 토큰은 파일명이다.
        String fileName = tokenizedLine.nextToken();

        // 기본적으로 루트(/)로부터 주소가 시작하므로 제거한다.
        if(fileName.startsWith("/") == true)
        {
          if(fileName.length() > 1)
          {
            fileName = fileName.substring(1);
          }
          // 파일명을 따로 입력하지 않았을 경우 기본 파일을 출력한다.
          else
          {
            fileName = DEFAULT_FILE_PATH;
          }
        }

        File file = new File(fileName);

        // 요청한 파일이 존재하는가?
        if(file.exists())
        {
          // 존재하는 파일의 MIME타입을 분석한다.
          String mimeType = new MimetypesFileTypeMap()
            .getContentType(file);

          // 파일의 바이트수를 찾아온다.
          int numOfBytes = (int) file.length();

          // 파일을 스트림을 읽어들일 준비를 한다.
          FileInputStream inFile = new FileInputStream(fileName);
          byte[] fileInBytes = new byte[numOfBytes];
          inFile.read(fileInBytes);

          // 정상적으로 처리가 되었음을 나타내는 200 코드를 출력한다.
          outToClient.writeBytes("HTTP/1.0 200 Document Follows \r\n");
          outToClient.writeBytes("Content-Type: " + mimeType + "\r\n");

          // 출력할 컨텐츠의 길이를 출력
          outToClient.writeBytes("Content-Length: " + numOfBytes + "\r\n");
          outToClient.writeBytes("\r\n");

          // 요청 파일을 출력한다.
          outToClient.write(fileInBytes, 0, numOfBytes);
        }
        else
        {
          // 파일이 존재하지 않는다는 에러인 404 에러를 출력하고 접속을 종료한다.
          System.out.println("Requested File Not Found : " + fileName);

          outToClient.writeBytes("HTTP/1.0 404 Not Found \r\n");
          outToClient.writeBytes("Connection: close\r\n");
          outToClient.writeBytes("\r\n");
        }
      }
      else
      {
        // 잘못된 요청임을 나타내는 400 에러를 출력하고 접속을 종료한다.
        System.out.println("Bad Request");

        outToClient.writeBytes("HTTP/1.0 400 Bad Request Message \r\n");
        outToClient.writeBytes("Connection: close\r\n");
        outToClient.writeBytes("\r\n");
      }

      connectionSocket.close();
      System.out.println("Connection Closed");
    }
    // 예외 처리
    catch(IOException ioe)
    {
      ioe.printStackTrace();
    }
  }
}

* index.html

<html>
<head>
<title>웹서버 테스트</title>
</head>
<body>
<p>http://theeye.pe.kr</p>
<img src="sooji.jpg" />
</body>
</html>

 

사용자 삽입 이미지

테스트를 해보니 정상적으로 파일을 전송하고 해당 html에 딸려있는 객체들역시 정상적으로 전송됨을 알 수 있습니다. 잘 되네요~^^b

[샘플코드 다운로드]

[Java] 문자열 형태의 날짜(Date)를 원하는 형태로 바꾸기

자바에서는 날짜나 시간을 핸들링 하기 위해 Date라는 훌륭한 클래스를 제공하고 있지만 클라이언트가 서버와 통신할떄 XML/JSON등을 이용한다거나 하면 아무래도 해당 데이터형을 그대로 유지하기가 힘듭니다.

마치 모뎀 시절의 암호화/복호화가 필요하듯이 객체들을 시리얼라이징해서 문자열로 만들어내고 클라이언트에서는 데이터를 받아서 파싱이라는 과정을 거쳐 다시 사용가능한 형태의 데이터로 변환을 해야 합니다.

이때에 넘겨받은 문자열형태의 날짜 혹은 시간을 어떻게 변환할수 있을지 생각하여 만들어본 간단한 메서드입니다.
[code java]/**
 * <pre>
 * 문자열 형태의 날짜를 원하는 형태로 변환합니다.
 *
 * 예시)
 * “yyyy.MM.dd G ‘at’ HH:mm:ss z” 2001.07.04 AD at 12:08:56 PDT
 * “EEE, MMM d, ”yy” Wed, Jul 4, ’01
 * “h:mm a” 12:08 PM
 * “hh ‘o”clock’ a, zzzz” 12 o’clock PM, Pacific Daylight Time
 * “K:mm a, z” 0:08 PM, PDT
 * “yyyyy.MMMMM.dd GGG hh:mm aaa” 02001.July.04 AD 12:08 PM
 * “EEE, d MMM yyyy HH:mm:ss Z” Wed, 4 Jul 2001 12:08:56 -0700
 * “yyMMddHHmmssZ” 010704120856-0700
 * “yyyy-MM-dd’T’HH:mm:ss.SSSZ” 2001-07-04T12:08:56.235-0700
 * </pre>
 *
 * @param date 변환할 날짜
 * @param fromFormatString 변환될 포맷
 * @param toFormatString 변환할 포맷
 * @return 변환된 날짜 문자열
 */
public static String formattedDate
(String date, String fromFormatString, String toFormatString)
{
SimpleDateFormat fromFormat =
new SimpleDateFormat(fromFormatString);
SimpleDateFormat toFormat =
new SimpleDateFormat(toFormatString);
Date fromDate = null;

try
{
fromDate = fromFormat.parse(date);
}
catch(ParseException e)
{
fromDate = new Date();
}

return toFormat.format(fromDate);
}

/**
 * <pre>
 * 날짜를 원하는 형태의 문자열로 반환합니다.
 * </pre>
 *
 * @param date 변환할 Date 인스턴스
 * @param format 변환할 포맷
 * @return 변환된 날짜 문자열
 */
public static String formattedDate(Date date, String format)
{
SimpleDateFormat toFormat = new SimpleDateFormat(format);
return toFormat.format(date);
}[/code]
이제 사용해 봅시다. 다음과 같이 간단하게 출력하고자 하는 형태로 변환하여 사용하시면 됩니다.
[code java]formattedDate(“2010-12-25”, “yyyy-MM-dd”, “yyMMdd”);
formattedDate(new Date(), “yyyy-MM-dd”);[/code]