CS/네트워크

웹 서버에 도착한 데이터를 수신하고 처리하는 방법

JWonK 2023. 7. 28. 18:32
728x90
반응형

23. 서버의 개요


 

23-1. 클라이언트와 서버의 차이점

서버 머신은 용도에 따라 다양한 종류가 있으며, 하드웨어나 OS 부분은 클라이언트와 다른 것도 있다.

그러나 네트워크에 관한 부분, 즉 LAN 어댑터, 프로토콜 스택, Socket 라이브러리 등의 기능은 클라이언트와 조금도 다르지 않다.

TCP나 IP의 기능은 하드웨어나 OS가 무엇이든지 달라지지 않기 때문에 기능이 통일되었다고 해도 무방하다.

 

원래 가지고 있는 기능은 같아도 사용하는 방법까지 같은 것은 아니다.

  • 접속 동작을 할 때 클라이언트에서 접속 동작을 수행하고, 서버는 그것을 기다리는 형태가 되므로 Socket 라이브러리의 사용법이 조금 달라진다.
  • 또한 서버의 애플리케이션은 동시에 다수의 클라이언트 PC와 대화한다는 차이점도 있다. 그렇기 때문에 서버 애플리케이션은 클라이언트 애플리케이션과 구조가 다르다.

 

 

 

23-2. 서버 애플리케이션의 구조

  • 서버는 동시에 복수의 클라이언트와 통신 동작을 실행하지만, 하나의 프로그램으로 여러 클라이언트들의 상대를 처리하기는 어렵다.
  • 어느 클라이언트와 어디까지 대화가 진행되고 있는지를 전부 파악해야 하기 때문이다.
  • 그래서 클라이언트가 접속할 때마다 새로 서버 프로그램을 작동하여 서버 애플리케이션이 클라이언트와 1 대 1로 대화하는 방법을 선택하는 것이 일반적이다.

 

 

클라이언트마다 다른 부분에서 데이터를 주고 받는다.

 

  1. 서버 프로그램을 만든다. 아래와 같이 둘로 나누어 만든다.
    1. 서버 프로그램을 접속을 기다리는 부분
    2. 클라이언트와 대화하는 부분
  2. 서버 프로그램을 작동해서 설정 파일 읽기 등의 초기화 동작을 마쳤을 때 접속을 기다리는 부분(a)을 실행한다.
  3. 이 부분이 소켓을 작성하고 소켓을 클라이언트에서의 접속 동작을 기다리는 상태로 만든 채 쉬는 상태가 된다.
  4. 클라이언트가 접속했을 때 다시 작동하여 접속을 기다린다. 
  5. 클라이언트와 대화하는 부분(b)을 작동시켜서 그곳에 접속이 끝난 소켓을 건네주고 동작을 계속하면 클라이언트와 대화하는 부분(b)은 접속이 끝난 소켓을 사용하여 클라이언트와 대화하기 시작한다.
  6. 대화가 끝나면 연결을 끊고 이 부분을 종료한다.

 

  • 클라이언트와 대화하는 부분(b)은 새 클라이언트가 접속할 때마다 잇달아 가동되므로 (b)의 부분이 한 대의 클라이언트와 1 대 1로 대응한다.
  • 이렇게 하면 (b)의 부분은 다른 클라이언트의 일에 신경쓰지 않고 자신에게 대응하는 클라이언트만 상대하면 된다.
  • 서버 OS는 멀티태스크 또는 멀티스레드라는 기능에 의해 다수의 프로그램을 동시에 함께 작동할 수 있다.

 

 

 

 

23-3. 서버측의 소켓과 포트 번호

서버 애플리케이션이 Socket 라이브러리를 호출하는 부분의 개요를 정리해보자.

먼저 다양한 형태로 데이터를 송·수신하는 구조를 지원하려면 데이터 송·수신의 구조에서 클라이언트와 서버라는 식으로 역할을 결정하지말고 좌우 대칭으로 어디에서나 자유롭게 데이터를 송신할 수 있도록 해야한다. TCP의 배경에는 이러한 개념이 있다.

 

어떻게 해도 좌우 대칭으로 만들 수 없는 부분이 있는데, 이것이 접속 동작이다. 

접속 동작은 한쪽이 기다리고 있는 곳에 또 한쪽을 연결해야 한다. 양쪽이 동시에 접속하면 안되며, 양쪽에서 기다려도 안되고, 기다리고 있지 않은 곳에 일방적으로 접속하는 것도 안된다.

 

데이터 송·수신 동작의 시점에서 보았을 때 클라이언트와 서버의 차이점은 여기에 있다. 즉 접속하는 측이 클라이언트고, 접속을 기다리는 쪽이 서버이다.

 

이 경우 Socket 라이브러리를 호출하는 부분에 다음과 같은 차이점이 있다. 먼저 클라이언트의 데이터 송·수신 동작은 다음의 네 단계로 성립한다.

 

(1) 소켓을 만든다 [소켓 작성 단계]

(2) 서버측의 소켓과 파이프로 연결한다 [접속 단계]

(3) 데이터를 송·수신한다 [송·수신 단계]

(4) 파이프를 분리하고 소켓을 말소한다 [연결 끊기 단계]

 

이에 비해 서버쪽은 (2) 접속 부분이 접속을 기다리는 형태가 되므로 아래와 같이 된다.

 

(1) 소켓을 만든다 [소켓 작성 단계]

(2-1) 소켓을 접속 대기 상태로 만든다 [접속 대기 상태]

(2-2) 접속을 접수한다 [접속 접수단계]

(3) 데이터를 송·수신한다 [송·수신 단계]

(4) 파이프를 분리하고 소켓을 말소한다 [연결 끊기 단계]

 

 

[서버의 동작]

서버 애플리케이션의 통신 동작

 

  1. 먼저 socket을 호출하여 소켓을 만든다.
  2. bind를 호출하여 소켓에 포트 번호를 기록한다 [2-1]
  3. 클라이언트측에서 접속 동작을 실행할 때 서버측의 소켓에 할당한 포트 번호를 지정하는데, 이것이 포트 번호이다.
  4. 포트 번호를 기록하면 listen을 호출하여 소켓에 접속하기를 기다리는 상태라는 제어 정보를 기록한다 [2-1]
  5. 이렇게 해서 소켓은 클라이언트에서 접속 동작의 패킷이 도착하는 것을 기다리는 상태가 된다
  6. 그러면 accept를 호출하여 접속을 접수한다 [2-2]
  7. 접속을 접수하는 부분은 서버 애플리케이션을 기동한 후 즉시 실행되므로 이 시점에서는 아직 클라이언트의 접속 패킷이 도착하지 않았을 것이다.
  8. 패킷이 도착하지 않았지만 accept를 한 것은 도착하자마자 바로 접수 동작을 실행하기 위함이다
  9. 이 상태에서 접속 패킷이 도착하면 응답 패킷을 반송하여 접속 동작을 실행하고, 접속 대기의 소켓을 복사하여 새로운 소켓을 만들고, 접속 상대의 정보를 비롯한 제어 정보를 새 소켓에 기록한다 [아래 그림] 여기까지가 accept를 호출했을 때의 동작으로, 이렇게 새 소켓이 클라이언트 측의 소켓과 연결
  10. accept가 끝나면 접속을 기다리는 동작은 끝나므로 그 후 접속을 접수하는 부분은 클라이언트와 대화하는 부분을 기동

 

서버측 소켓

 

 

[소켓을 식별하기 위해 디스크립터를 사용하는 이유]

  • 접속 대기의 소켓에는 클라이언트 측의 IP 주소와 포트 번호가 기록되어 있지 않기 때문
  • 디스크립터라는 한 개의 정보로 식별하는 쪽이 간단하기 때문

 

 

 

 

 

 

 

 

 

24. 서버의 수신 동작


 

24-1. TCP/IP의 패킷 수신 동작

클라이언트의 패킷 수신 동작과 같고 한 번 더 복습한다.

 

[패킷의 수신 동작]

  1. 패킷의 신호를 LAN 어댑터에서 수신하고 디지털 데이터로 바꾸는 부분에서 시작한다. 패킷 신호 : 이진 데이터 + 클록 신호
  2. 따음에 패킷의 맨 마지막에 있는 프레임 체크 시퀀스(FCS)라는 오류 검사용 데이터를 이용하여 오류 유무를 검사한다.
  3. FCS가 일치하고 오류가 없는 것을 확인한 다음에는 맨 앞의 MAC 헤더에 있는 수신처 MAC 주소를 조사하여 패킷이 자신을 수신처로 하여 보낸 것인지 판단한다.
  4. 수신처가 자신으로 되어 있는 패킷만 남기고 다른 패킷은 버린다.
  5. 신호를 수신하여 디지털 데이터로 되돌리는 동작은 끝났으므로 디지털 데이터로 되돌린 것을 LAN 어댑터 내부의 버퍼 메모리에 저장한다. 여기까지 LAN 어댑터의 MAC 부분이 실행한다.
  6. CPU는 패킷을 알아채지 못하므로 인터럽트라는 방법을 사용하여 도착을 알린다.
  7. CPU는 인터럽트에 의해 LAN 드라이버를 동작하고 LAN 어댑터의 버퍼 메모리에서 수신한 패킷을 추출한다.
  8. 그리고 MAC 헤더의 타입필드의 값에 따라 프로토콜을 판별하고, 프로토콜을 처리하는 소프트웨어를 호출한다.
  9. 타입 필드의 값은 IP 프로토콜인 것을 나타내는 값으로 되어있으므로 TCP/IP의 프로토콜 스택을 호출하고 여기에 패킷을 건네준다
  10. 프로토콜 스택에 패킷이 전달되면 우선 IP 담당 부분이 동작하여 IP 헤더를 점검한다. IP 헤더 내용이 규칙에 따라 올바르게 만들어졌는지부터 점검한 후 수신처 IP 주소가 자신을 대상으로 하는지 조사
  11. 서버에서 라우터와 같이 패킷을 중계하는 기능이 유효하게 된 경우 패킷 중계
  12. 조각 나누기에 의해 패킷이 분할되었는지 조사
  13. 분할된 패킷의 조각이 전부 도착한 시점에서 패킷 조각을 조립하여 복원
  14. IP 헤더의 프로토콜 번호 항목을 조사하여 해당하는 담당 부분에 패킷 건네줌 (TCP or UDP)
  15. TCP 담당 부분의 동작은 도착한 패킷의 내용에 따라 달라진다.
  16. 패킷의 TCP 헤더에 있는 SYN이라는 컨트롤 비트가 1로 되어 있으면 접속 동작의 패킷이다.
    1. 이 경우 접속하는 동작을 실행하는데, 그 전에 도착한 패킷의 수신처 포트 번호를 조사하여 이 번호와 같은 번호를 할당한 접속 대기 상태의 소켓이 있는지 확인
    2. 만약 수신처 포트 번호와 같은 대기 소켓이 없으면 오류 통지 패킷을 클라이언트에 반송
    3. 해당하는 접속 대기 소켓이 있으면 패킷을 복사하여 새 소켓을 만들고, 여기에 송신처의 IP 주소나 포트 번호, 시퀀스 번호의 초기값, 윈도우의 값 등 필요한 정보 기록. 동시에 송신 버퍼나 수신 버퍼로 사용하는 메모리 영역 확보
    4. 패킷을 받았음을 나타내는 ACK 번호, 서버에서 클라리언트에 보내는 데이터에 관한 시퀀스 번호의 초기값, 클라이언트에서 서버로 보내온 데이터를 받기 위한 수신 버퍼의 빈 용량을 나타내는 윈도우 값 등의 항목을 기록한 TCP 헤더를 만들고, 이것을 IP 담당 부분에 의뢰하여 클라이언트에 반송
    5. 이 패킷이 클라이언트에 도착하면 클라이언트에서 패킷을 받았음을 나타내는 ACK 번호가 돌아온다. 이것으로 접속 동작이 완료된다.
    6. 이때 서버측은 accept를 호출하여 실행을 쉬는 상태일 것이고 여기에 새로 만든 소켓의 디스크립터를 전달하여 서버 애플리케이션의 동작을 재개한다.
  17. 데이터 송·수신 단계에 들어가서 데이터 페킷이 도착한 경우의 동작
    1. TCP 담당 부분은 도착한 패킷이 어느 소켓에 해당하는지 조사한다. IP 헤더의 송신처 IP 주소와 수신처 IP 주소, TCP 헤더의 수신처 포트 번호와 송신처 포트 번호 4개 정보가 모두 합치되는 소켓을 찾는다.
    2. 발견하면 패킷에 기록되어 있는 데이터 송·수신의 진행 상황과 도착한 패킷의 TCP 헤더의 정보를 결합하여 데이터 송·수신 동작이 올바르게 진행되고 있는지 점검한다. (시퀀스 번호나 데이터 조각 길이를 계산해놓고 TCP 헤더 기록과 일치하는지)
    3. 패킷에서 데이터 조각을 추출하여 수신 버퍼에 저장한다. 이때 지난 번 패킷에서 수신한 데이터 조각의 다음에 연결되는 식으로 해서 데이터를 분할하기 전의 상태로 되돌린다.
    4. 이 과정을 통해 수신한 데이터를 수신 버퍼에 저장하면 수신 확인 응답용 TCP 헤더를 만든다.
    5. 수신 패킷의 시퀀스 번호와 데이터 조각의 길이로부터 계산한 ACK 번호를 기록하고, IP 담당 부분에 의뢰하여 클라이언트에 반송
    6. 데이터 패킷을 수신했을 때의 동작은 수신 버퍼에 데이터 조각을 보관한 부분에서 일단 끝난다.
    7. 이후 애플리케이션이 Socket 라이브러리의 read를 호출하여 애플리케이션에 건네줌
  18. 이후 제어가 서버 애플리케이션으로 넘어가서 받은 데이터를 처리하는 즉 HTTP의 리퀘스트 메시지의 내용을 조사하고 처리하는 과정

 

※ LAN 어댑터의 MAC 부분이 패킷을 신호로부터 디지털 데이터로 되돌리고 FCS를 점검한 후 버퍼 메모리에 저장

 

※ LAN 드라이버가 MAC 헤더로부터 프로토콜을 판단하여 프로토콜 스택에 패킷을 건네줌

 

※ 프로토콜 스택의 IP 담당 부분은 IP 헤더를 점검하고 (1) 자신을 대상으로 한 것인지 판단한 후 (2) 조각 나누기에 의한 패킷의 분할이 있는지 조사하고 (3) TCP 담당 부분 또는 UDP 담당 부분에 패킷을 건네줌

 

※ 패킷이 접속 동작의 패킷인 경우 TCP 담당 부분은 (1) TCP 헤더의 SYN의 컨트롤 비트를 확인하고 (2) 수신처 포트 번호를 조사한 후 (3) 해당하는 접속 대기 소켓을 복사하여 새 소켓을 작성하고 (4) 송신처의 IP 주소나 포트 번호 등을 기록

 

※ 데이터의 패킷을 수신한 경우 TCP 담당 부분은 (1) 도착한 패킷의 송신처 IP 주소, 송신처 포트 번호, 수신처 IP 주소, 수신처 포트 번호로부터 해당하는 소켓을 판단하고 (2) 데이터의 조각을 연결해서 수신 버퍼에 보관한 후 (3) 클라이언트에게 ACK를 되돌려준다.

 

 

 

 

 

24-2. TCP 담당 부분의 연결 끊기 동작

 

  1. Socket 라이브러리의 close를 호출하고, TCP 담당 부분이 FIN이라는 컨트롤 비트에 1을 설정한 TCP 헤더를 만든 후 IP 담당 부분에 의뢰하여 클라이언트에 보냄
  2. 클라이언트에 도착하면 클라이언트는 ACK 번호를 반송한다
  3. 클라인어트가 close를 계속 호출하고, FIN을 1로 한 TCP 헤더를 서버에 보낸 후 서버가 ACK 번호를 반송하면 연결 끊기 동작은 끝난다

 

 

 

 

 

 

 

25. 웹 서버 소프트웨어가 리퀘스트 메시지의 의미를 해석하여 요구에 응한다.


 

25-1. 리퀘스트 메시지를 보낸 웹 서버는 다음과 같이 동작한다

 

  1. 웹 서버는 먼저 URI의 부분에 쓰여있는 파일명을 조사하여 이것이 프로그램인지 판단한다
  2. 파일이 프로그램인 것을 알고 있으면 웹 서버는 이 프로그램을 작동시키도록 OS에 의뢰한다.
  3. 리퀘스트 메시지에서 데이터를 추출하여 작동시키는 프로그램에 건네준다.
  4. 작동시킨 프로그램이 받은 데이터를 처리하여 무언가의 출력 데이터를 웹 서버에 되돌려준다.

 

 

 

 

25-2. 웹 서버로 수행하는 액세스 제어

리퀘스트 메시지의 내용에서 데이터 출처를 판단하고, 그곳에서 데이터를 얻어 클라이언트에 반송한다는 것이 웹 서버의 기본 동작이다.

하지만 조건에 해당하는 경우만 동작을 실행하도록 하는 것이 있는데 이와 같이 조건에 따라 액세스 동작 여부를 설정하는 기능을 액세스 제어라고 한다.

 

웹 서버에서 설정하는 조건은 주로 다음 세 가지 정보이다.

 

(1) 클라이언트의 주소

(2) 클라이언트의 도메인명

(3) 사용자명과 패스워드

 

이 조건을 데이터 출처가 되는 파일이나 디렉토리와 대응해서 설정한다.

 

 

[조건이 설정되어 있을 때 동작]

도메인명에 기초하여 액세스 제한

 

  • 클라이언트의 도메인명이 조건으로 설정되어 있는 경우 클라이언트의 IP 주소에서 도메인명을 조사하는데, 이때 DNS 서버를 이용

 

  1. 클라이언트에서 리퀘스트 메시지를 받은 웹 서버는 프로토콜 스택에 의뢰하여 패킷의 송신처 IP 주소를 조사한다.
  2. IP 주소에 대응하는 이름을 조회하는 메시지를 만들어 가장 가까운 DNS 서버에 보낸다
  3. 그러면 DNS 서버는 이 IP 주소가 등록된 DNS 서버를 찾아 거기에 조회를 보낸다
  4. 여기에서 도메인명을 알 수 있을 것이므로 도메인명의 회답이 돌아온다.
  5. 회답이 돌아왔으면 웹 서버 측의 DNS 서버는 이것을 웹 서버에 전송한다
  6. 이를 통해 송신처 IP 주소에서 도메인명을 판명하고, 만일에 대비하여 도메인명에서 IP 주소를 조사한 후 송신처 IP 주소와 일치하는 것을 확인

→ 도메인명을 위조하여 DNS 서버에 등록하는 공격 방법도 있으므로 이것을 방지하기 위함

 

 

 

 

25-3. 응답 메시지를 되돌려 보낸다

이렇게 해서 리퀘스트 메시지에 대해 적절하게 처리하고 처리가 완료되면 응답 메시지를 반송한다.

 

  1. 웹 서버가 Socket 라이브러리의 write를 호출하여 응답 메시지를 프로토콜 스택에 건네준다.
  2. 이때 응답 메시지를 어디에 보내야 할지를 프로토콜 스택에 알려주어야 하는데, 통신 상대의 클라이언트를 직접 통지하는 것이 아니라 어느 소켓을 사용하여 통신하고 있는지를 나타내는 디스크립터를 통지하여 상대를 지정해야 한다.
  3. 그러면 프로토콜 스택은 데이터를 한 개의 패킷에 들어가는 길이로 분할하고 헤더를 붙여서 패킷을 송출한다. 이 패킷에는 수신처로 클라이언트의 주소가 기록되어 있기 때문
  4. 이 패킷은 스위치나 라우터를 경유하여 인터넷 속을 통해 최종적으로 클라이언트에게 도착
728x90
반응형