'성공과 실패를 결정하는 1%의 네트워크 원리' 스터디를 진행하며 정리한 내용이다.
01 서버의 개요
-
클라이언트와 서버의 차이점
-
공통점
- 네트워크에 관한 부분, 즉 LAN 어댑터, 프로토콜 스택, Socket 라이브러리 등의 기능은 클라이언트와 조금도 다르지 않다.
-
차이점
- 서버 머신은 용도에 따라 다양한 종류가 있고, 하드웨어나 OS 부분은 클라이언트와 다른 것도 있다.
-
Socket 라이브러리의 사용법이 조금 다르다.
- 접속 동작을 할 때 클라이언트에서 접속 동작을 수행하고, 서버는 그것을 기다리는 형태가 되므로
- 서버의 애플리케이션은 동시에 다수의 클라이언트 PC와 대화한다.
-
-
서버 애플리케이션의 구조
-
클라이언트가 접속할 때마다 새로 서버 프로그램을 작동하여 서버 애플리케이션이 클라이언트와 1 대 1로 대화하는 방법을 선택하는 것이 일반적.
- 서버 프로그램을 접속을 기다리는 부분과 클라이언트와 대화하는 부분 둘로 나누어 만든다.
- 서버 프로그램을 작동해서 설정 파일 읽기 등의 초기화 동작을 마쳤을 때 접속을 기다리는 부분을 실행한다. 소켓을 작성하고 소켓을 클라이언트에서의 접속 동작을 기다리는 상태로 만든 채 쉬는 상태가 된다.
- 클라이언트가 접속했을 때 다시 작동하여 접속을 기다린다.
- 클라이언트와 대화하는 부분을 작동시켜서 그곳에 접속이 끝난 소켓을 건네주고 동작을 계속하면 클라이언트와 대화하는 부분은 접속이 끝난 소켓을 사용하여 클라이언트와 대화하기 시작한다.
- 대화가 끝나면 연결을 끊고 이 부분을 종료한다.
- 클라이언트와 대화하는 부분은 새 클라이언트가 접속할 때마다 잇달아 기동되므로 한 대의 클라이언트와 1 대 1로 대응한다.
- 서버 OS는 멀티태스트 또는 멀티스레드라는 기능으로 다수의 프로그램을 동시에 함께 작동할 수 있는데, 이 성질을 이용한 프로그래밍 기법임.
-
클라이언트가 접속했을 때 새로 프로그램을 기동하는 부분에서 다소 시간이 걸리고, 응답 시간이 추가로 소요된다는 단점이 있음.
- 미리 몇 개의 부분을 작동시켜 두고 클라이언트가 접속했을 때 클라이언트의 상대를 처리하지 않는 비어있는 것을 찾아 여기에 접속한 소켓을 건네주어 클라이언트와 대화를 계속하는 방법도 있음. (스레드풀)
-
-
서버측의 소켓과 포트 번호
-
데이터를 송, 수신하는 동작의 관점에서 보면 클라이언트와 서버라는 상태로 역할을 고정시키는 것은 좋은 방법이라고 할 수 없음.
- 클라이언트에서 서버에 액세스한다는 형태의 애플리케이션이 다수이지만, 다른 형태로 액세스하는 애플리케이션도 있음.
- 역할을 결정하지 말고 좌우 대칭으로 어디에서나 자유롭게 데이터를 송신할 수 있도록 해두는 방법이 좋음 (TCP 배경에는 이러한 개념이 있음)
-
다만, 접속 동작은 좌우 대칭으로 만들 수 없다.
- 데이터 송, 수신 동작의 시점에서 보았을 때 접속하는 측이 클라이언트, 접속을 기다리는 측이 서버이다.
-
Socket 라이브러리를 호출하는 부분에서 서버측의 구체적인 동작은 이러하다.
- socket을 호출하여 소켓을 만든다.
-
bind를 호출하여 소켓에 포트 번호를 기록한다.
- 구체적인 번호는 규칙에 의해 서버 애플리케이션마다 결정되어 있고, 웹 서버의 경우는 80번으로 되어 있다.
- listen을 호출하여 소켓에 접속하기를 기다리는 상태라는 제어 정보를 기록한다.
-
accept를 호출하여 접속을 접수한다.
- 패킷이 도착하지 않았는데도 접속 접수 동작을 실행하는 이유는 이렇게 하지 않으면 패킷이 도착할 때 그제서야 접속 접수 동작을 실행하기 때문.
- 패킷이 도착하면 응답 패킷을 반송하여 접속 접수 동작을 실행
- 접속 대기의 소켓을 복사하여 새로운 소켓을 만들고, 접속 상대의 정보를 비록한 제어 정보를 새 소켓에 기록한다. 새 소켓이 클라이언트측의 소켓과 연결된다.
- 그 후 클라이언트와 대화하는 부분을 기동.
- 접속 대기 상태인 소켓이 계속 존재나는 이유는 접속 대기 소켓에 그대로 접속하게 되면 이 소켓이 없어져 버리므로 다음에 다른 클라이언트가 접속하면 곤란해지기 때문.
-
클라이언트측에서 회답이 돌아왔을 때 다른 포트 번호의 소켓이 오면 접속 패킷을 보낸 상대로부터 돌아왔는지 아닌지를 판별할 수 없기에 새로 만든 소켓에도 접속 대기 소켓과 같은 포트 번호를 할당해야 함.
- 소켓을 지정할 때 클라이언트측의 IP 주소, 클라이언트측의 포트 번호, 서버측의 IP 주소, 서버측의 포트 번호 네 가지 정보를 사용함.
-
그렇다면 이 네 가지 정보를 사용하면 되니까 디스크립트는 필요없지 않을까?
-
소켓을 식별하기 위해 디스크립터를 사용하는 이유가 두 가지 있음
- 접속 대기의 소켓에는 클라이언트측의 IP 주소와 포트 번호가 기록되어 있지 않기 때문.
- 디스크립터라는 한 개의 정보로 식별하는 쪽이 간단하기 때문.
-
-
02 서버의 수신 동작
-
LAN 어댑터에서 수신 신호를 디지털 데이터로 변환한다.
-
패킷의 신호를 LAN 어댑터에서 수신하고 디지털 데이터로 바꾼다.
- LAN을 흐르는 패킷의 신호는 1,0으로 이루어진 디지털 데이터의 신호와 타이밍을 나타내는 클록 신호를 합성한 것이므로 클록 신호를 추출하고 클록 신호에서 타이밍을 계산하면서 신호를 읽어오면, 1,0 디지털 데이터로 바꿀 수 있다.
-
패킷의 맨 마지막에 있는 프레임 체크 시퀀스(FCS)라는 오류 검사용 데이터를 이용하여 오류 유무를 검사한다.
- FCS가 일치하지 않으면 데이터가 변했다는 뜻이니 패킷을 버린다.
-
MAC 헤더에 있는 수신처 MAC 주소를 조사하여 패킷이 자신을 수신처로 하여 보낸 것인지 판단한다.
- 아닌 경우 패킷을 버린다.
- 디지털 데이터로 되돌린 것을 LAN 어댑터 내부의 버퍼 메모리에 저장한다.
- 서버 CPU는 패킷을 알아차리지 못하니 인터럽트라는 방법을 사용하여 LAN 어댑터에서 CPU로 패킷의 도착을 알린다.
- LAN 드라이버가 MAC 헤더로부터 프로토콜을 판단하여 프로토콜 스택에 패킷을 건네준다.
-
-
IP 담당 부분의 수신 동작
-
IP 담당 부분이 동작하여 IP 헤더를 점검해 수신처 IP 주소가 자신을 대상으로 하는지 조사한다.
- 서버에서 라우터와 같이 패킷을 중계하는 기능이 유효하게 된 경우 자신을 대상으로 하지 않은 패킷이 도착할 수도 있으니 다시 패킷을 중계한다.
-
자신을 대상으로 한 것이 확인되면 조각 나누기에 의해 패킷이 분할되었는지 조사한다.
- 분할되어 있으면 메모리에 저장했다 전부 도착하면 원래 패킷으로 복원한다.
- 분할되어 있지 않으면 패킷 조립 동작은 필요 없으므로 패킷을 받은 것이 된다.
- IP 헤더의 프로토콜 번호 항목을 조사하여 해당하는 담당 부분에 패킷을 건네준다. (TCP 또는 UDP)
-
-
TCP 담당 부분이 접속 패킷을 수신했을 때의 동작
- 패킷의 TCP 헤더에 있는 SYN이라는 컨트롤 비트가 1로 되어 있으면 접속 동작의 패킷이다.
-
도착한 패킷의 수신처 포트 번호를 조사하여 이 번호와 같은 번호를 할당한 접속 대기 상태의 소켓이 있는지 확인한다.
- 없으면 오류 통지 패킷을 클라이언트에 반송함.
- 있으면 패킷을 복사하여 새 소켓을 만들고 여기에 송신처 IP 주소, 포트 번호, 시퀀스 번호의 초기값, 윈도우 값 등 필요한 정보를 기록한다.
- 동시에 송신 버퍼나 수신 버퍼로 사용하는 메모리 영역을 확보함.
- 패킷을 받았음을 나타내는 ACK 번호, 시퀀스 번호 초기값, 윈도우 값 등의 항목을 기록한 TCP 헤더를 만들고, 이를 IP 담당 부분에 의뢰하여 클라이언트에 반송함.
- 클라이언트가 이를 받았으면 ACK 번호를 보내고 접속 동작은 완료됨.
-
TCP 담당 부분이 데이터 패킷을 수신했을 때의 동작
- IP 헤더의 송신처 IP 주소, 수신처 IP 주소, TCP 헤더의 수신처 포트 번호, 송신처 포트 번호라는 4개의 정보가 모두 합치되는 소켓을 찾는다.
-
소켓을 발견하면 패킷에 기록되어 있는 데이터 송, 수신 진행 상황과 도착한 패킷의 TCP 헤더의 정보를 결합하여 데이터 송, 수신 동작이 올바르게 진행되고 있는지 점검한다.
- 구체적으로 시퀀스 번호를 사용해서 조사함.
- 제대로 도착한 것이면 데이터 조각을 수신 버퍼에 저장하고 지난 번 패킷에서 수신한 데이터 조각의 다음에 연결되어 분할되기 전 상태로 되돌린다.
- 수신 확인 응답용 TCP 헤더를 만든다. 여기에 수신 패킷의 시퀀스 번호, 데이터 조각의 길이로부터 계산한 ACK 번호를 기록하고, IP 담당 부분에 의뢰하여 클라이언트에 반송한다.
- read를 호출하여 수신한 데이터를 받아온 부분에서 애플리케이션에 건네준다.
-
TCP 담당 부분의 연결 끊기 동작
- 웹의 경우 HTTP 프로토콜의 버전에 따라 다른데 HTTP 1.0이라면 서버에서 연결 끊기 동작을 시작한다.
- 서버측에서 close를 호출하고, TCP 담당 부분이 FIN이라는 컨트롤 비트에 1을 설정한 TCP 헤더를 만든 후 IP 담당 부분에 의뢰하여 클라이언트에 보낸다.
- 클라이언트에 도착하면 클라이언트는 ACK 번호를 반송한다.
- 클라이언트가 close를 계속 호출하고, FIN을 1로 한 TCP 헤더를 서버에 보낸 후 서버가 ACK 번호를 반송하면 연결 끊기 동작은 끝남.
- 연결 끊기 동작이 끝나면 잠시 기다렸다가 소켓을 말소함.
03 웹 서버 소프트웨어가 리퀘스트 메시지의 의미를 해석하여 요구에 응한다.
-
조회의 URI를 실제 파일명으로 변환한다.
- 웹 서버의 경우 read에서 받은 데이터의 내용이 HTTP 리퀘스트 메시지가 된다.
-
받은 리퀘스트 메시지에 기록되어 있는 내용에 따라 적절한 처리를 실행하여 응답 메시지를 만들고, write를 통해 이것을 클라이언트에 반송한다는 형태로 작동함.
- 메소드나 URI의 내용에 따라 웹 서버 내부의 동작이 달라짐
-
단순히 URI에 기록되어 있는 파일을 디스크에서 읽는 것은 아니다.
- 디스크의 파일을 전부 액세스할 수 있게 되므로 웹 서버의 디스크가 무방비 상태로 노출되어 위험하기 때문.
- 웹 서버에서 공개하는 디렉토리는 가상으로 만든 디렉토리이고, 이 가상의 디렉토리 구조에서의 경로명을 URI에 써야함.
- 파일을 읽어올 때는 가상의 디렉토리와 실제 디렉토리의 대응 관계를 조사하고, 실제 디렉토리의 경로명으로 변환한 후 파일을 읽어 데이터를 반송함.
- 파일명을 바꿔쓰는 규칙을 서버측에 설정하고 규칙에 따라 파일명을 바꿔쓰고 나서 파일에 액세스하는 웹 서버 애플리케이션도 있다.
-
CGI 프로그램을 작동하는 경우
- 프로그램 파일의 이름을 URI에 쓸 수도 있다.
- 이 경우에는 해당 프로그램을 작동시켜서 프로그램이 출력하는 데이터를 클라이언트에 반송한다.
-
CGI라는 타입의 프로그램은 다음과 같이 동작한다.
- 무언가의 데이터를 리퀘스트 메시지 안에 넣어 브라우저에서 웹 서버로 보낸다.
- 웹 서버에서 URI 파일명을 조사하여 프로그램인지 판단한다.
- 파일이 프로그램인 것을 알고 있으면 웹 서버는 프로그램을 작동시키도록 OS에 의뢰한다.
- 작동시킨 프로그램이 데이터를 처리하여 무언가의 출력 데이터를 웹 서버에 돌려준다.
- 출력 데이터는 보통 HTML 태그를 내장한 HTML 문서로 되어 있으므로 웹 서버는 이것을 그대로 응답 메시지로 클라이언트에 반송한다.
-
웹 서버로 수행하는 액세스 제어
- 웹 서버 동작을 실행할 때 사전에 설정해 둔 조건에 해당하는지 조사하고, 조건에 해당하는 경우 그 동작을 금지하거나 조건에 해당하는 경우만 동작을 실행한다는 기능도 있다. 이와 같이 조건에 따라 액세스 동작 여부를 설정하는 기능을 액세스 제어라고 한다.
- 웹 서버에서 설정하는 조건은 주로 다음과 같다.
- 클라이언트의 주소
- 클라이언트의 도메인명
- 사용자명과 패스워드
- 클라이언트의 IP 주소가 조건으로 설정되어 있는 경우는 accept로 접속을 접수했을 때 클라이언트의 IP 주소를 알 수 있으므로 이것을 점검하기만 한다.
-
클라이언트의 도메인명이 조건으로 설정되어 있는 경우에는 IP 주소에서 도메인명을 조사하는데, 이 때 DNS 서버를 이용한다.
- 이 방법은 DNS 서버의 조회 메시지가 왕래하는 만큼 시간이 걸리며, 그만큼 응답 시간이 길어진다.
-
사용자명과 패스워드가 설정된 경우
- 보통의 리퀘스트 메시지에는 사용자명과 패스워드가 없으니 웹 서버는 사용자명과 패스워드를 기록하거나 리퀘스트 메시지를 보내도록 응답 메시지에서 클라이언트에 통지한다.
- 브라우저는 사용자명과 패스워드를 입력하는 화면을 표시한다.
- 리퀘스트 메시지에 기록하고, 다시 한 번 서버에 액세스 한다.
- 통지된 사용자명과 패스워드와 사전에 설정한 것을 대조하여 액세스 가능 여부를 판단하고, 액세스를 허가하는 경우에는 데이터를 반송한다.
-
응답 메시지를 되돌려 보낸다.
- 리퀘스트 메시지에 대해 적절하게 처리하고 처리가 완료되면 응답 메시지를 반송한다.
- 웹 서버가 write를 호출하여 응답 메시지를 프로토콜 스택에 건네준다.
-
디스크립터를 통지하여 상대를 지정한다.
- 소켓에는 통신의 상태가 전부 기록되어 있고, 여기에 통신 상대의 정보도 있으므로 디스크립터만 통지하면 된다.
04 웹 브라우저가 응답 메시지를 받아 화면에 표시한다.
-
응답 데이터의 형식을 보고 본질을 판단한다.
- 화면 표시 동작은 응답 메시지에 저장된 데이터가 어떤 종류인지를 조사하는 곳부터 시작한다.
- 데이터의 종류를 판단하는 근거는 몇 가지가 있는데, 응답 메시지의 맨 앞부분에 있는 'Content-Type'이라는 헤더 파일의 값으로 판단하는 것이 원칙.
- 압축 기술이나 부호화 기술에 따라 원래 데이터를 변환하고 나서 메시지에 저장한 경우에는 어떤 변환을 했는지를 Content-Encoding 필드에 기록해야 한다.
-
웹 서버가 Content-Type의 값을 정확하게 설정해야 하는데 현실은 그렇지 않은 경우도 있다. 그러므로 원칙에 따라 Content-Type을 조사만 해서는 데이터의 종류를 정확하게 판단할 수 없는 경우도 있다.
- 다른 판단 근거를 사용하여 종합적으로 데이터의 종류를 판단하는 경우도 있다. 확장자나 데이터 내용의 포맷 등에서 종합적으로 판단하는 것.
-
브라우저 화면에 웹 페이지를 표시하여 액세스를 완료한다.
-
HTML 문서, 일반 텍스트, 화상이라는 기본적인 데이터는 브라우저 자체가 화면 표시 기능을 가지고 있으므로 브라우저가 자체에서 화면 표시 동작을 실행한다.
- 실제 화면 표시 동작은 OS가 담당하므로 OS에 대해 화면의 어떤 위치에, 어떤 문자를, 어떤 글꼴로 표시할지 지시하는 것.
- 웹 페이지에 화상 등을 내재한 것의 경우 태그를 사용해 화상이 내장되었음을 나타낸다. 이 태그를 발견하면 브라우저는 화상 데이터의 파일을 서버에서 읽어온다.
- 데이터가 반송되면 태그가 쓰여있던 장소에 화상 데이터를 내장시킨다.
- HTML 문서나 화상과 같이 브라우저가 자체에서 표시 기능을 가지고 있는 경우에는 이렇게 해서 OS에 지시를 내리며 화면에 표시한다.
- 자체에 표시할 수 없는 경우 해당 애플리케이션을 호출한다.
-