[정보 및 잡담] 웹 프로토콜


Development note/Etc.  2015. 6. 17. 01:15

안녕하세요. 명월입니다.


우리는 웹서핑을 하면서 웹서비스를 인터넷 브라우저를 통해 이용한다는 것을 모르는 사람은 없을 것 입니다. 모른다면 지금 제 블로그를 볼 수가 없겠죠...ㅎㅎ


저번 포스트까지 소켓을 통해서 서버, 클라이언트를 나누고 정보를 주고 받는 것을 공부했는데, 브라우저의 경우는 이 클라이언트라고 생각하면 될 듯 싶네요.
클라이언트, 즉 브라우저는 웹 서버로 부터 HTML문서와 이미지, CSS, Javascript 파일등을 다운 받아서 그 다운받은 파일을 각자의 역할에 맞게 분석하고 화면에 뿌려주는(?) 표현해주는 역할을 담당하고 있습니다. 화면에 뿌리다, 표현하다라는 말이 조금 애매한데 어떻게 보면 가장 정확한 말인 듯 싶네요..
일단 이 말보다 더 정확하게 알아보아야 할 것은 브라우저는 서버로 소켓접속을 하고, 서버는 브라우저에서 요청하는 프로토콜(규약)을 받아서 해당하는 문서와 파일을 브라우저로 전송를 하면 되겠습니다.
그림으로 표시한다고 하면 아래와 같은 형태가 되겠네요.


웹의 구조


출처 - 링크


지금까지 웹 서버와 브라우저의 관계에 대해서 공부했습니다. 그러면 좀 더 웹 서버와 브라우저의 내부 처리는 어떻게 처리 되는지 공부하겠습니다.


통상적으로 서버와 브라우저가 통신하기 위해서는 서로간의 약속, 규약이 필요합니다. 웹서버와 웹브라우저 통신에도 그런 통신 규약이 존재를 하는 데 이를 우리는 웹 프로토콜이라고 부르고 HTTP라고 표현합니다. 정확히는 hypertext transport protocol이죠.. 이는 이 두 프로그램 간에 통신을 하기 위한 규약입니다. 그러면 규약의 내용을 알아보겠습니다.


프로토콜


출처 - 링크


위는 요청 프로토콜의 예로 첫번째 줄에는 GET, POST의 매소드 형태, 그 다음은 요청하는 페이지, 그리고 HTTP 버전의 정보가 있네요.
그 두번째 줄부터 Encoding 방식 등등의 많은 정보가 포함되어 있습니다.


브라우저에서 서버로 요청하는 프로토콜이 위와 같다면 다음은 서버로 부터 받는 클라이언트 응답 프로토콜에 대해 알아보겠습니다.


프로토콜


출처 - 링크


위는 서버에서 응답하는 메시지로서 모든 문서의 헤더부분에 프로토콜이 쓰이며 브라우저는 위 해더 프로토콜을 제외한 본문부분을 브라우저로 표현하겠습니다.


여기까지 프로토콜에 대해 간략하게 공부를 하였고 그럼 지금부터는 서버 소켓을 이용해서 웹 서버를 만들어 보겠습니다.

import java.io.IOException;
import java.net.ServerSocket;
import java.util.ArrayList;

public class Server extends ServerSocket implements Runnable{
    private ArrayList<Client> clients = null;
    public Server(int port) throws IOException {
        super(port);
        clients = new ArrayList<Client>();
    }
    public Client accept() throws IOException{
        if (isClosed())
            throw new IOException("Socket is closed");
        if (!isBound())
            throw new IOException("Socket is not bound yet");
        Client s = new Client(this);
        implAccept(s);
        s.start();
        return s;
    }
    @Override
    public void run() {
        while(true){
            try{
                clients.add(this.accept());
                System.out.println("Connection");
            }catch(IOException e){
                e.printStackTrace();
            }
        }
    }
    public void start(){
        (new Thread(this)).start();
    }
    public void deleteClient(Client client){
        clients.remove(client);
    }
}
import java.io.File;
import java.io.FileInputStream;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.Socket;

public class Client extends Socket implements Runnable{

    private Server parent = null;
    private OutputStream sender = null;
    private InputStream receiver = null;
    public Client(Server server){
        parent = server;
    }
    @Override
    public void run() {
        try{
            int size = 0;
            byte[] data = new byte[1024];
            String strBuffer = "";
            while(size = receiver.read(data)) > 0){
                strBuffer += new String(data);
                byte[] buffer = strBuffer.replace("\0","").getBytes();
                if(buffer[buffer.length -4] == 13 && buffer[buffer.length -3] == 10 &&
                    buffer[buffer.length -2] == 13 && buffer[buffer.length -1] == 10){
                    break;
                }
                System.out.println(strBuffer);
                String message = "HTTP/1.1 200 OK\r\n"
                        + "Server: MyServer\r\n"
                        + "Cache-Control: no-store, no-cache, must-revalidate\r\n"
                         + "Content-Length: 10\r\n"
                        + "Keep-Alive: timeout=15, max=100\r\n"
                        + "Connection: Keep-Alive\r\n"
                        + "Content-Type: text/html\r\n\r\n";
                message = "helloworld";
                sender.write(message.getBytes());

            }
        }catch(Exception e){
            e.printStackTrace();
        }finally{
            try{
                this.close();
            }catch(Exception e){
                e.printStackTrace();
            }
            parent.deleteClient(this);
        }
    }

    public void start(){
        try{
            initialize();
            (new Thread(this)).start();
        }catch(Exception e){
            e.printStackTrace();
        }
    }
    private void initialize() throws Exception{
        sender = getOutputStream();
        receiver = getInputStream();
    }
}
public class Main {
    public static void main(String[] args){
        try{
            new Main();
        }catch(Exception e){
            e.printStackTrace();
        }
    }
    public Main() throws Exception{
        Server server = new Server(80);
        server.start();
    }
}



Client.java 소스를 보면 30번째 줄에서 36번째 줄은 프로토콜의 정의가 되어있네요...

헤더와 본문의 구분은 "\r\n\r\n"로 구분이 되며 메시지의 종료도 "\r\n\r\n"로 구분로 구분이 됩니다. 위와 같이 프로그램이 작성이 되면 브라우저를 통해서 결과화면을 확인 하겠습니다.

웹의 구조


브라우저에서 확인한 결과 helloworld라는 메시지가 도착했습니다.
이번에는 서버쪽 Console을 확인 해 보겠습니다.



브라우저로 부터 프로토콜 정보가 제대로 왔네요....


관련소스 - github 바로가기