다음은 성공과 실패를 결정하는 1%의 네트워크 원리 를 읽고 정리한 내용입니다. 본 글은 CH6. 웹 서버에 도착하여 응답 데이터가 웹 브라우저로 돌아간다 입니다 🙌



🛺 [Story1] 서버의 개요

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

  • 서버도 클라이언트로부터 전송된 패킷을 받기 위해서 준비 단계를 거쳐야한다.

서버와 클라이언트의 차이점

  • 네트워크와 관련된 전체적인 구조는 비슷한 형태를 지니고 있다.
  • 하지만 서버는 소켓을 미리 열고 클라이언트의 연결을 기다린다는 점, 여러 클라이언트와 소통해야 한다는 점에서 클라이언트와 차이점을 가지고 있다.

2. 서버 어플리케이션의 구조

서버 프로그램에서 다수의 클라이언트와 소켓 통신을 하기 위해서 다음과 같은 구조로 진행한다.

  • 서버 프로그램에서 클라이언트의 접속을 기다리는 부분과
  • 클라이언트와 대화를 하는 부분을 나눈다.

클라이언트와 대화를 하는 부분은 각 클라이언트와 1대1로 대화를 한다. 따라서 대화가 섞이지 않는다.

  • 서버 OS의 멀티태스크, 멀티스레드 기능을 사용한 기법이다.
  • 이때 접속할 때마다 새로운 연결을 맺는것이 부담이 되면 클라이언트와의 연결 동작을 미리 해놓고 비어있는 소켓에 연결하기도 한다.

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

  • 서버와 클라이언트의 역할이 고정되어 있는 것이 아니라 여러 형태로 변경되면서 데이터를 송수신 할 수 있기 때문에 좌우대칭으로 실행될 수 있도록 하는 것이 중요하다.
  • 하지만 TCP 접속 동작은 좌우 대칭으로 만들기 어렵다. 한쪽이 소켓을 열고 기다리며 다른 한쪽이 연결을 해야하기 때문이다.
    • 여기서 접속하는 측이 클라이언트, 접속을 기다리는 측이 서버라고 할 수 있다.
  • 클라이언트의 데이터 송수신 동작
    1. 소켓 작성 - 소켓 생성
    2. 서버측의 소켓과 파이프로 연결 - 소켓 접속
    3. 데이터 송수신
    4. 파이프 분리 후 소켓 말소
  • 서버의 데이터 송수신 동작
    1. 소켓 작성 - 소켓 생성
    2. 소켓을 접속 대기 생태로 만듬
    3. 접속을 접수
    4. 데이터 송수신
    5. 파이프 분리 후 소켓 말소
    • 서버의 접속 준비 단계에서는 서버 어플리케이션의 포트 번호를 소켓에 기록한다. (웹 서버의 경우에는 80)
  • 서버에서 accept 를 통해서 클라이언트로부터 소켓을 접수한다.
  • 클라이언트와의 접속이 되면 접속 대기 소켓을 복사하여 새로운 소켓을 생성한다.
    • 접속 상대에 대한 제어 정보를 새로운 소켓에 기록한다.
  • 기존의 접속 대기 소켓은 계속 접속 대기인 상태로 존재하여 다른 클라이언트와의 접속을 기다린다.

웹 소켓의 포트번호

  • 본래 클라이언트는 80이라는 포트의 소켓과 통신을 시작한다. 하지만 접속 한 후 응답에 다른 포트 번호가 들어올 수 없으므로 그대로 80 포트를 사용해야 한다.
    • 이때 같은 포트 번호를 지닌 여러 소켓이 있게되는 문제가 발생한다.
    • 다음 패킷이 왔을 때 헤더의 포트번호로 통신 중인 소켓을 식별할 수 없기 때문이다.
  • 해결방법
    • 소켓을 지정할 때 다음 4가지 정보를 모두 조합하여 소켓을 식별한다.
      1. 클라이언트 IP주소
      2. 클라이언트 포트 번호
      3. 서버 IP주소
      4. 서버 포트 번호
  • 그렇다면 왜 디스크립터가 필요할까 ? (위 4가지 정보로 소켓을 식별할 수 있는데)
    • 접속을 대기할 때는 아직 클라이언트 소켓이 지정되지 않았으므로 정보가 없다.
    • 또한 디스크립터 하나로 소켓을 식별하는 것이 더 간단하다.

🛺 [Story2] 서버의 수신동작

1. LAN 어댑터에서 수신 신호를 디지털 데이터로 변환한다

  • LAN 어댑터에서 수신하여 디지털 데이터로 바꾼다. (클록 신호를 추출하여 데이터를 0101의 형태로 바꾼다)
  • FCS를 통해 패킷의 오류 유무를 검사한다. 오류가 있다면 패킷을 폐기한다.
  • MAC헤더의 수신처가 본인인지 확인한다.
    • 이더넷은 LAN 전체에 신호를 흘리고 상대를 찾는 형태이기 때문에 올바른 패킷이 아니더라도 도착할 수 있기 때문이다.
  • 변환된 디지털 데이터를 LAN 어댑터 내부의 버퍼 메모리에 저장한다. (여기까지 LAN의 MAC 부분이 담당한다)
  • LAN 어댑터에서 CPU에게 인터럽트로 패킷이 도착했다는 사실을 알린다.
  • LAN 드라이버는 LAN 어댑터의 버퍼 메모리에서 수신한 패킷을 추출하여 프로토콜을 판별하고 적합한 프로토콜 처리 소프트웨어를 호출한다. (TCP/IP 면 해당 프로토콜을 호출하여 패킷을 건낸다)

2. IP 담당 부분의 수신 동작

  • IP 담당은 IP 헤더를 점검하고 수신처가 본인 IP인지 확인한다.
    • 서버가 라우터와 같이 패킷을 중개하는 역할일 수 있으므로 본인에게 온 패킷이 아닐수도 있다. 이 경우에는 패킷에 쓰인 수신처 IP 주소로 패킷을 중개해야한다.
  • IP 담당에서는 fragmentation을 통해서 패킷이 분할되었는지 조사하고 그렇다면 패킷을 일시적으로 메모리에 저장하고 패킷이 모두 모이면 복원한다.
  • IP 헤더의 프로토콜 번호를 조사하여 해당 부분에 패킷을 건내준다.
    • 06이면 TCP 담당, 11이면 UDP 담당 등등

3. TCP 담당 부분이 접속 패킷을 수신했을 때의 동작

  • 만일 도착한 패킷의 SYN 컨트롤비트가 1이라면 접속 동작의 패킷이다.
    • 접속을 접수하는 동작을 실행한다.
    • 포트 번호를 조사하여 해당 포트 번호와 일치하는 접속 대기 소켓이 있는지 확인한다.
      • 만일 없다면 오류이므로 오류 패킷을 클라이언트에게 반환한다.
  • 접속 대기 소켓을 찾으면 소켓을 복사하여 새로운 소켓을 만들고 송신처IP주소, 포트번호, 시퀀스 초기값, 윈도우 값등 필수 정보를 기록한다.
  • 패킷을 받았다는 ACK 번호, 서버의 시퀀스 초기값, 수신 버퍼 빈 용량인 윈도우 값 등을 기록한 TCP 헤더를 만들고 IP 담당에 의뢰하여 반송한다.
  • 패킷이 잘 도착하면 ACK 번호가 돌아와 접속이 완료된다.

4. TCP 담당 부분이 데이터 패킷을 수신했을 때의 동작

  • 서버측에 같은 포트 번호를 가진 소켓이 많으므로 앞에서 말한 4가지 정보로 적절한 소켓을 찾는다.
  • 해당 소켓에 기록된 정보들을 통해 올바르게 데이터 송수신이 이루어지고 있는지 확인한다.
    • 시퀀스 번호, 데이터 조각의 길이 등등을 확인한다.
  • 패킷에서 추출된 데이터를 수신 버퍼에 저장한다.
  • 데이터를 수신하면 수신 응답용 TCP 헤더를 만들고 수신 패킷에 시퀀스 번호와 데이터 길이로 얻은 ACK 번호를 기록하고 IP 담당에 넘겨 클라이언트에게 반송한다.
  • 수신 버퍼에 기록된 데이터들은 소켓 라이브러리의 read 함수 호출을 통해 어플리케이션에 넘겨진다.
  • 어플리케이션에서 http 리퀘스트 메시지 내용을 조사하고 브라우저에서 데이터를 반송한다.

5. TCP 담당 부분의 연결 끊기 동작

  • 어느 쪽(클라이언트 or 서버) 에서든 연결 끊기 동작에 들어가도 상관없다.
    • HTTP 1.0 이라면 서버에서 연결 끊기 동작을 시작한다.
  • 서버에서 close 함수를 호출하고 FIN 1로 설정한 TCP 헤더를 클라이언트에게 보낸다.
  • 클라이언트도  close 를 호출하고 FIN 1로 설정하여 반송하고 ACK도 보낸다.
  • 잠시 기다렸다가 소켓을 말소한다.