Tag Archives: Multithread

[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

[샘플코드 다운로드]