Bibbidi Bobbidi Boo
article thumbnail

* TIL/개념: 최대한 공식 문서 & 책을 기반으로 배운 내용을 정리

* 현재 취준생으로 풋내기 개발자가 쓰는 글입니다.

* 그러니 조언과 지적 및 훈수는 언제나 환영입니다! 댓글로 많이 달아주세요!

 

성공과 실패를 결정하는 1%의 네트워크 원리

  1. 웹 브라우저가 메시지를 만든다.
    1. HTTP 리퀘스트 메시지를 작성한다.
    2. 웹 서버의 IP 주소를 DNS 서버에 조회한다.
    3. 전 세계의 DNS 서버가 연대한다.
    4. 프로토콜 스택에 메시지 송신을 의뢰
  2. TCP/IP의 데이터를 전기 신호로 만들어 보낸다.
  3. 케이블의 앞은 LAN 기기였다.
  4. 액세스 회선을 통해 인터넷의 내부로!
  5. 서버측의 LAN에는 무엇이 있는가?
  6. 웹 서버에 도착하여 응답 데이터가 웹 브라우저로 돌아간다.


메시지를 웹 서버에 송신하도록 OS에 의뢰한다.

: OS에 의뢰할 때 어떤 방식으로 의뢰하고, 또 무엇을 해주는가?


데이터 송수신 동작 개요

IP 주소를 조사하고 나면, 웹 서버에 메시지를 송신하도록 프로토콜 스택에 의뢰한다.

이 때 어떻게 의뢰하는가?

 

결론부터 이야기하면

IP 주소를 조회했을 때처럼, Socket 라이브러리의 함수를 사용하되, 결정된 순서대로 호출한다.

 

Socket 라이브러리에서는 프로토콜 스택을 호출해 의뢰하게 된다.

그리고 컴퓨터 사이에는 파이프 같은 것을 통해 데이터를 양방향으로 주고 받는다.

이를 나타내면 아래와 같다.

파이프 같은 것을 통해 데이터가 흐른다.

  • 파이프라고 표현했지만, 결국 파이프로 연결하는 동작이 필요하다.
  • 연결되는 동작은 다음 순서로 진행된다.
    1. 먼저 서버측에서 데이터 출입구, 즉 소켓을 만들고, 소켓에 클라이언트가 파이프를 연결하기를 기다린다.
    2. 클라이언트에서 소켓을 만들고 파이프를 늘려 서브측의 소켓에 연결한다.
    3. 양쪽의 소켓이 연결되어 준비를 완료한다.
  • 소켓에서 데이터를 쏟아붓듯이 데이터 송수신 동작을 실행한다.
  • 송수신 동작이 끝나면 다음 순서로 종료한다
    1. 애플리케이션의 규칙에 따라 파이프를 분리한다.
    2. 한쪽에서 분리하면 다른 한쪽도 분리되고, 파이프를 분리하면 소켓을 말소해 통신 동작이 종료한다.

이는 다음처럼 4단계로 요약 가능하다.

<데이터 송수신 과정>
(1) 소켓을 만든다.(소켓 작성 단계)
(2) 서버측의 소켓에 파이프를 연결한다.(접속 단계)
(3) 데이터를 송수신한다.(송수신 단계)
(4) 파이프를 분리하고 소켓을 말소한다.(연결 끊기 단계)

이 동작은 OS 내부의 프로토콜 스택에서 진행된다.


데이터 송수신 동작 본론

전체적인 과정을 코드와 그림으로 나타내면 아래와 같다.

클라이언트와 서버의 메시지 송수신 동작

그렇다면 각각의 단계(소켓 작성 - 접속 - 송수신 - 연결 끊기)를 하나씩 살펴보자.


준비단계: 소켓 만들기

애플리케이션은 디스크립터라는 번호표 같은 것으로 소켓을 식별한다.

맨 처음은 아래 부분으로, socket을 만드는 함수를 호출한다.

<디스크립터> = socket(<IPv4 사용>, <스트림형>, …);

여기서 약간의 혼란을 느낄 수 있으므로 정의하면

  • socket - Socket 라이브러리에 포함된 함수
  • Socket - 라이브러리
  • 소켓 - 파이프 양끝에 있는 출입구

여기서는 라이브러리의 내부 동작 과정은 스킵하고(2장에서 설명)

socket()을 호출했을 때 디스크립터를 반환한다고 보자.

여기서 말하는 디스크립터는 소켓을 생성했을 때 반환값을 말한다.

  • 복수의 소켓이 발생할 때 소켓을 식별하기 위해 사용하는 것.

접속: 파이프 연결하기

디스크립터: 애플리케이션이 소켓을 식별하는 것
ip 주소와 포트 번호: 클라이언트와 서버 간에 상대의 소켓을 식별하는 것.

socket을 만들었으면 이제 파이프로 연결할 차례다.

코드 부분에서는 아래에 해당한다.

connect(<디스크립터>, <서버의 ip 주소와 포트 번호>, ...);

connect() 시에는 파라미터로 디스크립터, 서버의 ip 주소, 포트 번호를 넘겨준다.

  • 디스크립터: 소켓을 만들 때 돌아온 디스크립터
    • 프로토콜 스택은 어느 소켓을 서버측의 소켓에 접속할지 판단해 접속 동작을 실행
  • IP 주소: DNS 서버에 조회하여 조사한 액세스 대상의 IP 주소
    • 컴퓨터를 식별
  • 포트 번호: IP 주소와 일치하는 컴퓨터 내의 어느 소켓과 접속할지 지정
    • 디스크립터와는 무엇이 다른가?
      • 디스크립터: 애플리케이션에서 소켓을 식별하기 위해 사용
      • 포트 번호: 접속 상대 측에서 소켓을 식별하기 위해 사용
    • 그럼 디스크립터 없이 포트번호만 해도 되지 않는가? → 이는 나중에 공부(책, 393P)
    • 포트번호는 애플리케이션의 종류에 따라 미리 결정된 값을 사용한다.
      • 예) 웹: 80, 메일: 25
    • 서버에서 클라이언트 측의 소켓의 번호는 어떻게 알 수 있나?
      • 클라이언트의 소켓의 포트번호는 소켓을 만들 때 프로토콜 스택이 적당한 값을 골라 할당한다.
      • 그리고 프로토콜 스택이 접속 동작을 실행할 때 서버에 통지한다.

결국 요약하면 connect()를 호출하면 프로토콜 스택은 접속 동작을 실행한다.

  • 상대와 연결되면 프로토콜 스택은 연결된 상대의 ip 주소나 포트 번호 등의 정보를 소켓에 기록한다.
  • 이로서 데이터 송수신 가능한 상태가 된다.

송수신 단계: 메시지 주고받기

소켓이 상대측과 연결된 후,

소켓에 데이터를 쏟아부으면 상대측의 소켓에 데이터가 도착하게 된다.

 

가장 먼저 송신 시에는 아래 함수를 호출한다.

write(<디스크립터>, <송신 데이터>, <송신 데이터 길이>);

이 때 파라미터로 디스크립터와 송신 데이터를 지정한다.

이 과정에서 프로토콜 스택은 송신 데이터를 서버에게 송신한다.

  • 소켓에는 연결된 상대가 기록되어있다.
  • 때문에 디스크립터로 소켓을 지정해서 연결할 상대가 판명되어 그곳으로 데이터를 송신한다.
  • 해당 데이터는 네트워크를 통해 서버에 도착한다.
  • 서버는 수신 동작을 실행해 조사해 실행후, 응답 메시지를 반송한다.

 

서버가 반송한 데이터는 아래 함수를 호출함으로서 읽어들인다.

<수신 데이터 길이> = read(<디스크립터>, <수신 버퍼>, ...);

read()를 사용해 프로토콜 스택에 수신 동작을 의뢰한다.

이 때 수신한 응답 메시지를 저장하기 위해 메모리 영역, 즉 수신 버퍼를 지정한다.


연결 끊기 단계: 송수신 종료 

다음을 호출하면 연결 끊기 단계로 들어가도록 OS에 의뢰한다.

close(<디스크립터>);

이렇게 되면 소켓 사이를 연결한 파이프 같은 것이 분리되고, 소켓 또한 말소된다.

 

웹에서 사용하는 HTTP 프로토콜에서는 송신 완료 후 서버에서 연결 끊기를 실행하게 된다.

때문에 웹 서버 측에서 먼저 close()를 호출해 연결을 끊는다.

그리고 이는 클라이언트 측에 전달되어 클라이언트의 소켓 또한 연결 끊기로 들어간다.

  • read로 수신 동작을 의뢰할 때 데이터를 주는 대신, 연결이 끊겼다는 사실을 브라우저에게 통지한다.
  • 브라우저도 close를 호출해 연결을 끊는다.

요약

  • 브라우저가 데이터를 송수신할 때 OS의 프로토콜 스택에 의뢰하는 과정
    1. 소켓을 만든다.(소켓 작성 단계)
    2. 서버측의 소켓에 파이프를 연결한다.(접속 단계)
    3. 데이터를 송수신한다.(송수신 단계)
    4. 파이프를 분리하고 소켓을 말소한다.(연결 끊기 단계)

 

profile

Bibbidi Bobbidi Boo

@비비디

포스팅이 좋았다면 "좋아요❤️" 또는 "구독👍🏻" 해주세요!