[Network] Java로 간단한 HTTP 웹서버 구현하기


 

시험 기간이 끝나고 오랜만에 글을 쓰게 되었습니다. 이번 학기에 듣고 있는 수업 중에 컴퓨터 네트워크 관련 수업이 있는데, 웹서버 구현 과제가 있어서 과제를 하면서 공부한 것들을 정리해 보겠습니다.




웹 서버 (myServer.java) 코드


package org.ybin;
 
import java.io.BufferedReader;
import java.io.DataOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.net.ServerSocket;
import java.net.Socket;
 
public class myServer {
 
    // port 80은 root 계정에서만 허용
    // sudo su로 루트 계정으로 바꾸고 실행해야함.
    public static void main(String[] args) throws IOException {
        ServerSocket listener = new ServerSocket(80);
        try {
            while (true) {
                Socket socket = listener.accept();
                try {
                    System.out.printf("IP from : %s, Port : %d\n",
                            socket.getInetAddress(), socket.getPort());
                     
                    OutputStream o = socket.getOutputStream();
                    DataOutputStream dos = new DataOutputStream(o);

                    File accessFile = new File("/home/ubuntu/cn/ok.html");
                    int accessFileLength = (int)accessFile.length();

                    if(accessFile.exists()){
                        FileInputStream in = new FileInputStream(accessFile);
                        byte[] fBytes = new byte[accessFileLength];
                        in.read(fBytes);
                        in.close();

                        dos.writeBytes("HTTP/1.1 200 OK \r\n");
                        dos.writeBytes("Content-Type: text/html;charset=utf-8\r\n");
                        dos.writeBytes("Content-Length: " + accessFileLength + "\r\n");
                        dos.writeBytes("\r\n");
                        
                        dos.write(fBytes, 0, accessFileLength);
                        dos.writeBytes("\r\n");
                        dos.flush();
                    }
                    else{
                        dos.writeBytes("HTTP/1.0 404 Not Found \r\n");
                        dos.writeBytes("Connection: close\r\n");
                        dos.writeBytes("\r\n");
                        dos.flush();
                    }

                     
                    
                } finally {
                    socket.close();
                }
            }
        } finally {
            listener.close();
        }
    }
}






코드 분석


코드는 오라클 클라우드 서비스에서 작동하고 있으며, 오라클 클라우드의 public IP를 브라우저에 입력하면 미리 작성한 html 파일을 출력받게 됩니다만, 개인적인 이유로 IP는 공개하고 있지 않습니다. 코드 자체는 매우 간단합니다. 아래는 핵심적인 내용으로 나눠 분석하였으니 필요하신 분들은 참고해주세요.

ServerSocket listener = new ServerSocket(80);
     try {
        while (true) {
           Socket socket = listener.accept();
               try {
                   System.out.printf("IP from : %s, Port : %d\n",
                      socket.getInetAddress(), socket.getPort());
                            
일단 ServerSocket으로 포트 80번을 열어줍니다. while문으로 클라이언트의 요청을 계속 기다리면서, 요청이 들어오게 되면 socket.getInetAddress()와 socket.getPort()로 클라이언트의 IP와 포트를 출력합니다.
참고로 해당 코드는 오라클 클라우드에서 돌아가고 있는데, 80번 포트를 사용하기 위해선 root 계정으로 class 파일을 실행해야 합니다. 그렇지 않으면 80번 포트에 대한 permission denied가 발생합니다.



    OutputStream o = socket.getOutputStream();
    DataOutputStream dos = new DataOutputStream(o);

    File accessFile = new File("/home/ubuntu/cn/ok.html");
    int accessFileLength = (int)accessFile.length();

소켓의 OutputStream으로 o를 선언하고, 이를 이용해 dos를 선언합니다. Html 파일에 대한 정보는 DataOutputStream dos에 저장됩니다. ok.html 파일을 불러와서 이 파일의 내용을 dos에 쓸 것입니다.



if(accessFile.exists()){
	FileInputStream in = new FileInputStream(accessFile);
	byte[] fBytes = new byte[accessFileLength];
	in.read(fBytes);
	in.close();

	dos.writeBytes("HTTP/1.1 200 OK \r\n");
	dos.writeBytes("Content-Type: text/html;charset=utf-8\r\n");
	dos.writeBytes("Content-Length: " + accessFileLength + "\r\n");
	dos.writeBytes("\r\n");
                        
	dos.write(fBytes, 0, accessFileLength);
	dos.writeBytes("\r\n");
	dos.flush();
	}

accessFile이 존재하면 (제대로 가져왔다면) 파일을 byte 데이터로 변환합니다. dos에 response message 형식을 지정합니다. 처음에 버젼 정보 및 정상적으로 접속 (200) 했다는 정보를 주고, Content type과 Content length를 지정하고 Html 파일의 내용을 dos에 써줍니다.



else{
        dos.writeBytes("HTTP/1.0 404 Not Found \r\n");
        dos.writeBytes("Connection: close\r\n");
        dos.writeBytes("\r\n");
        dos.flush();
    }
파일을 제대로 가져오지 못할 경우에는 404 에러 코드를 발생시키고 소켓 연결을 끊어줍니다.



IP를 브라우저에서 입력해서 접속하면 위와 같은 화면을 만나볼 수 있습니다.






마치며..

사실은 이렇게 끝날 글이 아닙니다. HTTP Response Message 구조에 대한 설명도 없고, 클라우드 플랫폼에 대한 얘기도 없고, 소켓이 어떤 원리로 동작하는 지에 대한 얘기도 없고...
이론적인 얘기를 한다면 끊임 없지만, 이 글은 단지 Java로 WebSocket 등을 사용하지 않고 Http 페이지를 어떻게 직접 만들 수 있는지 간단히 소개하는 글일 뿐이므로 자세한 설명은 생략하겠습니다. 
사실 이론적인 부분은 구글링을 통해 쉽게 찾을 수 있습니다. 기회가 된다면 저 역시 포스팅을 해볼 생각이지만, 그게 언제가 될 지는 모르겠습니다.

혹시라도 필요한 정보가 있다면, 알고 있는 한에서 공유해볼 것이니, 댓글을 통해서 정보를 공유할 수 있으면 좋겠습니다. 또한 틀린 내용이나, 부족한 내용이 있다면 역시 댓글로 달아주세요. 저 역시 공부하는 입장이라 잘못된 내용은 항상 존재할 것이라 생각합니다. 그럼, 모두 즐거운 코딩하세요!





댓글