2018년 3월 24일 토요일

JAVA로 mini was 만들기

1.  클라이언트 요청 정보 출력

프로젝트를 만들고 'WASMain'이라는 Class를 만들고 소스를 아래와 같이 작성한다
서버에 8080포트를 열고 기다리는 Socket을 생성하고 클라이언트에서 전송한 정보를 출력하는 소스를 작성한다. 아래와 같이 작성 후 Main 메소드를 실행한다.
import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;

public class WASMain {
    public static void main(String[] args){
        ServerSocket listener = null;
        try{
            //특정 port에서 기다린는 ServerSocket을 생성한다.
            listener = new ServerSocket(8080);
            System.out.println("client를 기다립니다.");

            // 소켓서버 종료될때까지 무한루프
            while(true){
                // 클라이언트가 접속할때까지 기다린다.
                Socket client = listener.accept(); // 블러킹 메소드.
                System.out.println(client);
                handleSocket(client);
            }

        }catch(Exception ex){
            ex.printStackTrace();
        }finally { // finally부분에서 서버소켓을 close한다.
            try {
                listener.close();
            }catch(Exception e){}
        }
    }

    private static void handleSocket(Socket client) throws IOException {
        InputStream in = client.getInputStream(); //클라이언트에게서 받은 데이터를 처리를 위한 작업
        BufferedReader br = new BufferedReader(new InputStreamReader(in));

         //1. btye로 한번에 읽어서 출력하기
         /*byte[] buffer = new byte[1024];
         int count = 0;
         while((count = in.read(buffer)) != -1){
             System.out.write(buffer, 0 ,count);
         }*/

         //2. String변수에 담아서 출력 하기
         String line = "";
         while ((line = br.readLine()) != null){
             System.out.println(line);
         }


        in.close();
        client.close(); // 클라이언트와 접속이 close 된다.
    }
}

브라우저에서 'http://localhost:8080'에 접속한다. IDE 툴로 돌아가서 콘솔을 보면 아래와 같이 GET방식으로 요청이 온 정보들이 보일 것이다.(주소창에서 입력해서 접속하는 방식은 GET방식이다.

2.  Thead 처리 지원하는 WAS

listener.accept() 부분이 블로킹 메소드라서 요청이 올때 Blocking되는 문제가 있다.  Thread처리가 가능한 WAS로 변경하자.
Bolcking vs Nonbolcking
public class WASMain {
    public static void main(String[] args){
        ServerSocket listener = null;
        try{
            //특정 port에서 기다린는 ServerSocket을 생성한다.
            listener = new ServerSocket(8080);
            System.out.println("client를 기다립니다.");

            // 소켓서버 종료될때까지 무한루프
            while(true){
                // 클라이언트가 접속할때까지 기다린다.
                Socket client = listener.accept(); // 블러킹 메소드.
                System.out.println(client);

                new Thread(() -> { //jdk 1.8 Lamd형식
                   try{
                       handleSocket(client);
                   }catch (Exception ex){
                       ex.printStackTrace();
                   }

                }).start();
            }

        }catch(Exception ex){
            ex.printStackTrace();
        }finally { // finally부분에서 서버소켓을 close한다.
            try {
                listener.close();
            }catch(Exception e){}
        }
    }

    private static void handleSocket(Socket client) throws IOException {
        InputStream in = client.getInputStream(); //클라이언트에게서 받은 데이터를 처리를 위한 작업
        BufferedReader br = new BufferedReader(new InputStreamReader(in));

        //String변수에 담아서 출력 하기
        String line = "";
        while ((line = br.readLine()) != null){
            System.out.println(line);
        }

        in.close();
        client.close(); // 클라이언트와 접속이 close 된다.
    }
}

3. 파일 및 이미지 파일 만들기

브라우저에 출력하기 위해 파일과 이미지를 업로드 한다.
mac 기준으로 터미널에서 /tmp/wasroot/ 디렉토리를 생성한다.(tmp폴더는 재부팅시 초기화)
index.html을 만들고 아래와 같이 입력하고
<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8">
    <title>index.html</title>
  </head>
  <body>
    Hello World!
    <img src="hello.png" >
  </body>
</html>
/tmp/wasroot/ 경로에 이미지 파일을 복사한다.
hello.png 라는 이미지 파일이 /Users/mac/Downloads/ 경로에 있다고 가정하면 터미널에서 다음과 같이 명령어를 실행한다.
cp /Users/mac/Downloads/hello.png /tmp/wasroot/hello.png

4. 원하는 정보만 출력하기

HttpRequest라는 Class를 만들과 출력할 정보에 관한 set, get 메소드를 생성한다.

public class HttpRequest {
    private String method; //GET, POST, PUT, DELETE ....
    private String path;
    private String host; // host
    private int contentLength; //COntent-Length : body 길이
    private String userAgent; // User-Agent : 브라우저 정보
    private String contentType; // Content-Type : 사용자가 요청한 컨텐츠의 타입

    public String getMethod() {
        return method;
    }

    public void setMethod(String method) {
        this.method = method;
    }

    public String getPath() {
        return path;
    }

    public void setPath(String path) {
        this.path = path;
    }

    public String getHost() {
        return host;
    }

    public void setHost(String host) {
        this.host = host;
    }

    public int getContentLength() {
        return contentLength;
    }

    public void setContentLength(int contentLength) {
        this.contentLength = contentLength;
    }

    public String getUserAgent() {
        return userAgent;
    }

    public void setUserAgent(String userAgent) {
        this.userAgent = userAgent;
    }

    public String getContentType() {
        return contentType;
    }

    public void setContentType(String contentType) {
        this.contentType = contentType;
    }

    @Override
    public String toString() {
        return "HttpRequest{" +
                "method='" + method + '\'' +
                ", path='" + path + '\'' +
                ", host='" + host + '\'' +
                ", contentLength='" + contentLength + '\'' +
                ", userAgent='" + userAgent + '\'' +
                ", contentType='" + contentType + '\'' +
                '}';
    }
}
WASMain 클래스도 변경한다.

import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;

public class WASMain {
    public static void main(String[] args){
        ServerSocket listener = null;
        try{
            listener = new ServerSocket(8080);
            System.out.println("client를 기다립니다.");
            while(true) {
                Socket client = listener.accept(); // 블러킹 메소드.
                System.out.println(client);
                new Thread(() -> {
                    try {
                        handleSocket(client);
                    } catch (Exception ex) {
                        ex.printStackTrace();
                    }
                }).start();
            }

        }catch(Exception ex){
            ex.printStackTrace();
        }finally { // finally부분에서 서버소켓을 close한다.
            try {
                listener.close();
            }catch(Exception e){}
        }
    }

    private static void handleSocket(Socket client) throws IOException {
        OutputStream out = client.getOutputStream(); //클라이언트에게 데이터를 보내기 위한 작업
        PrintWriter pw = new PrintWriter(new OutputStreamWriter(out));

        InputStream in = client.getInputStream(); //클라이언트에게서 받은 데이터를 처리를 위한 작업
        BufferedReader br = new BufferedReader(new InputStreamReader(in));
        String line = null;
        HttpRequest request = new HttpRequest();
        line = br.readLine();
        String[] firstLineArgs = line.split(" ");
        request.setMethod(firstLineArgs[0]);
        request.setPath(firstLineArgs[1]);

        while((line = br.readLine()) != null){
            if("".equals(line)){ // 헤더를 읽고 빈줄을 만나면
                break;
            }
            String[] headerArray = line.split(" ");
            if(headerArray[0].startsWith("Host:")){
                request.setHost(headerArray[1].trim());
            }else if(headerArray[0].startsWith("Content-Length:")){
                int length = Integer.parseInt(headerArray[1].trim());
                request.setContentLength(length);
            }else if(headerArray[0].startsWith("User-Agent:")){
                request.setUserAgent(line.substring(12));
            }else if(headerArray[0].startsWith("Content-Type:")){
                request.setContentType(headerArray[1].trim());
            }
        }
        System.out.println(request);

        String baseDir = "/tmp/wasroot";
        String fileName = request.getPath();

        if("/".equals(fileName)){
            fileName = "/index.html";
        }else if(fileName.endsWith(".png")){ //png 이미지일때
            fileName = request.getPath();
        }else{ //잘못된 경로 처리
            fileName = "/error.html";
        }
        fileName = baseDir + fileName;

        String contentType = "text/html; charset=UTF-8";
        if(fileName.endsWith(".png")){
            contentType =  "image/png";
        }

        File file = new File(fileName); // java.io.File
        long fileLength = file.length();

        if(file.isFile()){
            pw.println("HTTP/1.1 200 OK");
            pw.println("Content-Type: " + contentType);
            pw.println("Content-Length: " + fileLength);
            pw.println();
        }else{
            pw.println("HTTP/1.1 404 OK");
            pw.println("Content-Type: " + contentType);
            pw.println("Content-Length: " + fileLength);
            pw.println();
        }

        pw.flush(); // 헤더와 빈줄을 char형식으로 출력

        FileInputStream fis = new FileInputStream(file);
        byte[] buffer = new byte[1024];
        int readCount = 0;
        while((readCount = fis.read(buffer)) != -1){
            out.write(buffer,0,readCount);
        }
        out.flush();


        out.close();
        in.close();
        client.close(); // 클라이언트와 접속이 close된다.
    }
}

5. 브라우저 접속

브라우저에서 'http://localhost:8080'에 접속한다.
콘솔과 브라우저에 화면에 아래와  같이 나오면 정상이다.
POST 방식은 크롬 웹 어플레케이션 'Restlet Client - REST API Testing'을 통해 TEST할수 있다.


6. 프로젝트 구조

client에서 서버로 요청하면 accpet()가 실행되고 작업을 thread에게 넘긴후 다시 대기 상태로 돌아간다.
* GitHub
Share:

0 개의 댓글:

댓글 쓰기