Home [네트워크 스터디] Chapter_02 TCP/IP의 데이터를 전기 신호로 만들어 보낸다
Post
Cancel

[네트워크 스터디] Chapter_02 TCP/IP의 데이터를 전기 신호로 만들어 보낸다

📗 Chapter_02 TCP/IP의 데이터를 전기 신호로 만들어 보낸다

🌟 2장의 요점 짚고 넘어가기 🌟

Chapter 02에선 OS에 내장된 프로토콜 스택이 처음 등장한다. 프로토콜 스택은 네트워크 제어용 소프트웨어이다. 프로토콜 스택은 브라우저에서 받은 메세지를 패킷 속에 저장하고, 수신처 주소 등의 제어 정보를 덧붙인다. 프로토콜 스택은 통신 오류가 발생했을 때 패킷을 고쳐서 보내거나 통신의 기본을 조절하는 등의 다양한 역할을 한다.

이뿐만 아니라 프로토콜 스택은 패킷을 LAN어댑터(이더넷이나 무선 LAN으로 통신할 때 사용하는 하드웨어)에 넘긴다. 그리고 LAN 어댑터가 패킷을 전기신호로 변환하고 LAN의 케이블에 송출하는 과정을 통해 패킷이 네트워크 속으로 전달된다.

1. 소켓을 작성한다.

1️⃣ 프로토콜 스택의 내부 구성

아래 이미지는 브라우저에서 데이터를 전송했을 때 어떠한 소프트웨어와 하드웨어를 거쳐 서버에 도착하는 지

간단하게 요약한 이미지이다.

image

✔️ 네트워크 어플리케이션

  • 브라우저, 메일러, 웹 서버, 메일 서버 등의 프로그램이 여기에 해당됨
  • 네트워크 어플리케이션부터 아래로 향하여 데이터 송 수신 등의 일을 의뢰함
  • Socket
    • 네트워크 애플리케이션 아랫부분에는 Socket 라이브러리가 존재하고 그 안에는 리졸버가 내장되어 있음
    • Socket 라이브러리는 DNS서버에서 목적지의 IP 주소를 조회하는 역할을 담당함

✔️  프로토콜 스택

  • 프로토콜 스택은 OS 내부에 존재하는 네트워크 제어용 소프트웨어이다.
  • TCP, UDP 프로토콜을 사용하는 소프트웨어
    • 프로토콜 스택의 윗부분에는 TCP, UDP 프로토콜을 사용하여 데이터 송수신을 담당하는 부분이 존재한다. 이 둘이 어플리케이션의 의뢰를 받아 송수신 동작을 실행한다.
  • IP 프로토콜을 사용하는 소프트웨어
    • 데이터를 작게 나눈 패킷을 통신 상대까지 운반하는 것이 해당 소프트웨어의 주 역할
    • IP 안에는 ICMP와 ARP라는 프로토콜을 다루는 부분이 존재함
    • ICMP는 패킷을 운반할 때 발생하는 오류를 통지하거나 제어용 메세지를 통지할 때 사용됨
    • ARP는 IP 주소에 대응하는 이더넷의 MAC 주소를 조사할 때 사용됨

✔️ LAN 드라이버

  • LAN 어댑터의 하드웨어를 제어함
  • LAN 드라이버 아래에 있는 LAN 어댑터가 실제 송 수신 동작, 즉 케이블에 대해 신호를 송 수신 하는 동작을 실행함
1
2
3
4
5
💡 프로토콜 스택이란 네트워크 제어용 소프트웨어이다. 프로토콜 스택은 제어 정보를 저장하거나 패킷을 LAN 어댑터에 넘기는 역할을 담당한다.

💡 브라우저나 메일 등의 일반적인 애플리케이션이 데이터를  수신할 경우에는 TCP를 주로 사용

💡 DNS 서버에 대한 조회 등에서 짧은 제어용 데이터를 송수신할 경우에는 UDP 사용

✔️  소켓의 메세지 송신 동작  

image

2️⃣ 소켓의 실체는 통신 제어용 제어 정보

프로토콜 스택은 내부에 제어 정보를 기록하는 메모리 영역을 가지고 있다. 해당 영역에는 통신 동작 제어에 필요한 제어 정보를 기록한다. 대표적으로 통신 상대의 IP주소, 포트번호, 통신 동작 진행 상태 등이 있다.

본래 소켓은 개념적인 것이고 실체가 존재하지 않는다. 굳이 말하자면 프로토콜 스택 내부에 저장된 제어 정보가 소켓의 실체라고 할 수 있다. 프로토콜 스택은 소켓에 기록된 제어 정보를 참조하면서 동작한다.

3️⃣ 소켓을 호출했을 때의 동작

✔️  socket()

  • 애플리케이션이 socket을 호출하여 프로토콜 스택에게 소켓을 만들 것을 의뢰한다. 이때 프로토콜 스택은 소켓 한 개 분량의 메모리 영역을 확보하고 초기 상태임을 나타내는 제어 정보를 기록한다. 이 과정을 통해 소켓이 생성된다.
  • 소켓이 생성되면 소켓을 나타내는 디스크립터를 애플리케이션에 알려준다. 디스크립터는 프로토콜 스택의 내부에 있는 다수의 소켓 중 어느 것을 가리키는지 나타내는 번호표와 같다. 디스크립터는 프로토콜 스택이 데이터 송 수신 동작을 의뢰할 때 통지한다.

2. 서버에 접속한다.

1️⃣  접속의 의미

이더넷이나 통신 회선은 항상 케이블이 연결되어 있으므로 데이터를 신호로 변환하여 송신하기만 하면 언제든 통신이 가능하다. 하지만 소켓을 만든 직후에는 아무런 정보도 기록되어 있지 않기 때문에 통신 상대와의 사이에 제어 정보를 주고받아서 데이터 송 수신이 가능한 상태로 만드는 작업이 필요하다. 여기에서 주고받는 제어정보는 IP나 포트번호 등이다. 접속 동작에서 주고받는 제어 정보는 통신의 규칙으로 정해져 있다.

접속을 시도할 때는 제어 정보를 주고받는 것 뿐만 아니라 송 수신 데이터를 일시적으로 저장할 버퍼 메모리 확보도 이때 같이 실행된다.

2️⃣ 맨 앞부분에 제어 정보를 기록한 헤더를 배치한다.

✔️  헤더란? 

  • 헤더는 클라이언트와 서버가 서로 연락을 절충하기 위해 주고받는 정보가 포함되어 있다.
  • 헤더에는 데이터를 송 수신 하는 동작이나 연결을 끊는 동작도 포함되어 있기 때문에 통신 동작 전체에서 필요한 내용을 검토하여 TCP 프로토콜의 사양으로 규정하고 있다.
  • 클라이언트와 서버 사이에 주고받는 패킷 맨 앞부분부터 부가된 제어정보를 헤더라고 한다.
  • 이더넷이나 IP에도 동일한 헤더가 존재하기 때문에 TCP 헤더, 이더넷 헤더(=Mac 헤더), IP헤더와 같이 구분하여 사용해야 한다.

✔️  소켓에 기록되는 정보

  • 프로토콜 스택의 동작을 제어하기 위한 정보
    • 애플리케이션에서 통지된 정보
    • 통신 상대로부터 받은 정보
    • 송 수신 동작의 진행 상황
  • 프로토콜 스택이 소켓에 기록된 정보를 참조하면서 움직이기 때문에 소켓의 제어 정보는 프로토콜 스택과 일체화 되어있다 해도 무방하다.
  • 소켓에 기록되는 정보는 상대측에서 볼 수 없다. 이미 서로 필요한 정보를 헤더로 주고받기 때문에 통신에는 문제가 없다.
1
2
3
4
🌟  통신 동작에 이용하는 제어 정보는 다음의  종류

1. 헤더에 기입되는 정보
2. 소켓에 기록되는 정보

3️⃣ 접속 동작의 실체

✔️ connet()

  • 여기에 서버측의 IP 주소와 포트 번호를 입력하면 명령이 프로토콜 스택의 TCP 담당 부분에 전달된다.
  • 그러면 TCP 담당 부분은 목적지 서버측의 TCP 담당 부분과의 사이에 제어 정보를 주고 받는다.

✔️  클라이언트와 서버의 대화 과정 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
1. 데이터  수신 동작의 개시를 나타내는 제어 정보를 기록한 헤더를 만든다.
    1. 헤더의 내용  중요한 것은 송신처와 수신처의 포트번호이다. 이를 통해 송신처와 수신처를 지정할  있다.
    
2. TCP 헤더가 만들어지면 이를 IP 담당 소프트웨어에 건네주어 송신하도록 의뢰한다.

3. 송신 동작을 거쳐 네트워크를 통해 서버에  패킷이 도착하면 서버측의 IP 담당 소프트웨어가 이를 TCP 담당 소프트웨어 에게 전달한다.

4. 서버측의 TCP 담당 소프트웨어가 TCP헤더를 조사하여 기록되어 있는 수신처 포트 번호에 해당하는 소켓을 찾아낸다. 
   해당하는 소켓이 발견되면 해당 소켓에 필요한 정보를 기록하고 접속 동작 상태를 진행 중으로 변경한다.
    
5. (4)번 과정이 끝나면 서버의 TCP 담당 소프트웨어는 응답을 돌려보내기 위해 IP 담당 소프트웨어에게 의뢰한다. 

6. 이때 ACK라는 컨트롤 비트도 1 설정하여 돌려보낸다. 

7. 패킷이 클라이언트에게 정상적으로 돌아왔으면 TCP 헤더를 조사하여 서버측의 접속 동작이 성공했는  확인한다. 
   서버 측의 접속이 성공했으면 클라이언트 소켓에 서버 IP 주소나 포트 번호 등과 함께 접속 완료를 나타내는 제어 정보를 기록한다.
    
8. 마지막으로 클라이언트에 패킷이 정상적으로 도착했다는 것을 알리기 위해 ACK 비트를 1 만든 TCP 헤더를 서버측에 반송한다.

🌟 ACK는 패킷이 대상에게 잘 도착했는지 확인하는 용도로 사용된다.

3. 데이터를 송수신 한다.

1️⃣ 프로토콜 스택에 HTTP 리퀘스트 메세지를 넘긴다.

connect에서 애플리케이션에 제어가 되돌아오면 데이터 송 수신 동작이 들어간다. 데이터 송 수신 동작은 애플리케이션이 ✔️ write() 를 호출하여 송신 데이터를 프로토콜 스택에 건네주는 곳부터 시작된다.

프로토콜 스택은 받은 데이터의 내용을 곧바로 송신하는 것이 아니라 일단 프로토콜 스택 내부의 송신용 버퍼 메모리 영역에 저장하고 애플리케이션이 다음 데이터를 건네주기를 기다린다.

송신용 버퍼에 저장하는 이유는 애플리케이션에서 프로토콜 스택에 건네주는 데이터의 길이는 애플리케이션마다 상이하기 때문이다. 이러한 상황에서 받은 데이터를 곧바로 보내는 방법은 작은 패킷을 많이 보낼 수 있지만 네트워크 이용 효율이 저하되므로 어느 정도 데이터를 저장하고 나서 송 수신 동작을 진행한다.

송신 버퍼에 어느 정도의 데이터가 담겼을 때 송신한다는 규칙은 OS의 종류나 버전마다 달라지지만 MTU타이밍을 바탕으로 어느정도 판단할 수 있다.

✔️ MTU?

  • 한 패킷으로 운반할 수 있는 디지털 데이터의 최대 길이 이더넷에서는 보통 1500 바이트
  • MTU에는 패킷의 맨 앞부분에 헤더가 포함되어 있음, 여기부터 헤더를 제외한 것이 하나의 패킷으로 운반할 수 있는 데이터의 최대 길이가 됨 이것을 MSS 라고 한다.

✔️ MSS?

  • 헤더를 제외하고 한 개의 패킷으로 운반할 수 있는 TCP의 데이터 최대 길이
  • 애플리케이션에서 받은 데이터가 MSS를 초과하거나 MSS에 가까운 길이에 이르기까지 데이터를 저장하고 송신동작을 진행하면 패킷이 잘게 나눠질 걱정을 하지 않아도 된다.

image

✔️ 타이밍?

애플리케이션의 송신 속도가 느려지는 경우 MSS에 가깝게 데이터를 저장하면 여기에서 시간이 걸려 송신 동작이 지연되므로 버퍼에 데이터가 모이지 않아도 적당한 곳에서 송신 동작을 실행해야 한다. 따라서 프로토콜 스택은 일정한 시간이 경과하면 패킷을 송신할 수 있게 내부에 타이머라는 소프트웨어를 둔다.

두 가지의 판단요소가 존재하지만 이 둘은 상반되는 면도 존재한다. MSS 바탕을 중시하면 패킷 길이가 길어져서 네트워크 이용 효율이 높아지지만 버퍼에 머무는 시간만큼 송신 동작이 지연될 우려가 존재한다. 반대로 타이밍을 중시하면 지연은 적어지지만 이용 효율이 떨어지므로 양자를 절충해서 적당히 시간을 가늠하여 송신 동작을 실행해야 한다. 그러나 TCP 프로토콜 사양에는 절충에 대한 규약은 없으므로 실제 판단은 프로토콜 스택을 만드는 개발자의 몫이다.

2️⃣ 데이터가 클 때는 분할하여 보낸다

HTTP 리퀘스트 메세지는 보통 그다지 길지 않은 것이 대부분이다. 하지만 항상 예외는 존재하기에 HTTP 리퀘스트 메세지가 한 개의 패킷에 전부 들어가지 못할 경우, 송신 버퍼에 들어있는 데이터를 맨 앞부터 차례대로 MSS의 크기에 맞게 분할하고 분할한 조각을 한 개씩 패킷에 넣어 송신한다.

image

3️⃣  ACK 번호를 사용하여 패킷이 도착했는지 확인한다.

데이터 송신동작은 앞서 설명한 것 만으로는 끝나지 않는다. TCP는 송신한 패킷이 상대에게 도착했는지 확인하고 도착하지 않았으면 다시 송신하는 기능이 있으므로 패킷을 송신한 후에는 확인 동작으로 넘어간다.

먼저 TCP 담당 소프트웨어는 데이터를 조각으로 분할할 때 조각이 통신 개시부터 따져서 몇 번째 바이트에 해당 하는지 세어둔다. 이 값을 시퀀스 번호라고 하며 TCP 헤더에 기록한다. 이렇게 시퀀스 번호를 기록할 경우 수신측에서 패킷의 누락여부를 확인할 수 있다. 수신측은 전달 받은 데이터의 바이트 크기를 확인하고 이를 ACK 번호에 기록하고 이 값에 1을 더하여 송신측에 전달한다.

✔️ 예시

총 4381바이트의 데이터를 전송한다고 가정한다.

[누락되지 않은 경우]

  1. 시퀀스 번호 1 , 데이터 크기 : 1460바이트
    1. ACK 번호 1461
  2. 시퀀스 번호 1461, 데이터 크기 : 1460바이트
    1. ACK 번호 2921
  3. 시퀀스 번호 2921, 데이터 크기 : 1460바이트
    1. ACK 번호 4381

[누락된 경우]

  1. 시퀀스 번호 1 , 데이터 크기 : 1460바이트
    1. ACK 번호 1461
  2. 시퀀스 번호 1461, 데이터 크기 : 1460바이트
    1. ACK 번호 전송하지 않음
  3. 시퀀스 번호 1461, 데이터 크기 : 1460바이트 (재전송)
    1. ACK 번호 2921
1
2
3
4
🌟  시퀀스 번호와 ACK 번호로 패킷이 수신측에 도착한 것을 확인한다.

🌟 ACK 번호를 통지할 때는 단순히 ACK 번호에 값을 설정할 뿐만 아니라 제어 비트의 ACK 비트도 1 설정한다. 
이렇게하면 ACK 번호 필드가 유효하다는 의미가 된다.

4️⃣  패킷 평균 왕복 시간으로 ACK 번호의 대기 시간을 조정한다.

ACK 번호가 돌아오는 것을 기다리는 시간을 타임아웃 값 이라고 한다.

네트워크가 혼잡하면 ACK 번호가 돌아오는 시간이 지연될 수 있다. 이때 ACK 번호가 돌아오기전에 패킷을 재전송하면 혼잡한 네트워크를 악화시키는 사태가 발생한다.

이러한 문제를 방지하기 위해 대기 시간을 적절하게 설정해야한다. 대기 시간이 너무 짧으면 패킷을 자주 보내게 되고 대기시간을 너무 길게 설정하면 패킷을 다시 보내는 동작이 지연되어 속도 저하의 원인이 되기 때문이다.

보통 TCP는 ACK 번호가 돌아오는 시간을 기준으로 대기 시간을 판단하여 동적으로 변경하는 방법을 취한다.

5️⃣  윈도우 제어 방식으로 효율적으로 ACK 번호를 관리한다.

ACK 번호가 돌아올 때까지의 시간동안 아무 일도 하지 않고 기다리는 것은 자원과 시간 낭비이다.

낭비를 줄이기 위해 TCP는 윈도우 제어 방식에 따라 송신과 ACK 번호 통지의 동작을 실행한다.

윈도우 제어 방식이란 한 개의 패킷을 보낸 후 ACK 번호를 기다리지 않고 차례대로 연속해서 복수의 패킷을 보내는 방법을 말한다. 이럴 경우 ACK 번호가 돌아올 때까지의 시간이 낭비되지 않는다.

주의할 점은 ACK 번호를 기다리지 않고 차례로 패킷을 보내면 수신측의 능력을 초과하여 패킷을 보내는 사태가 발생할 수 있다.

수신측은 패킷을 수신하면 수신측 버퍼 메모리에 일시 보관한다. 수신측에서는 ACK 번호를 계산하는 작업이나 조각을 연결하여 원래 데이터를 복원하고 애플리케이션에 넘겨주어야 수신 버퍼에 저장된 데이터가 사용되어 사라진다. 하지만 송신측이 수신측의 처리속도보다 빠른 속도로 패킷을 송신하면 수신 버퍼에 데이터가 쌓이지 않고 사라질 수 있다.

이러한 문제를 방지하기 위해 수신측은 송신측에 수신가능한 데이터의 양을 통지한다. 수신 가능한 양은 TCP 헤더의 윈도우 필드에 기록하여 송신측에 알린다.

6️⃣ ACK 번호와 윈도우를 합승한다.

수신측은 네트워크 효율성 저하를 방지하기 위해 ACK 번호나 윈도우를 통지할 때 소켓을 바로 보내지 않고 잠시 기다린다. 기다리는 사이에 통지 동작이 발생하면 양쪽을 상승시켜서 한 개의 패킷으로 묶어서 전송한다.

예를 들자면 ACK 번호의 송신을 대조할 때 윈도우 통지가 발생하면 ACK 번호와 윈도우를 한개의 패킷에 합승 시켜서 통지하여 패킷의 수를 줄이는 것이다.

연속으로 ACK 번호 통지가 발생할 때도 마찬가지다. ACK 번호는 수신한 데이터의 끝이 어디인지를 송신측에 알리는 것이므로 ACK 번호가 통지가 연속적으로 발생하면 마지막 ACK 번호만 송신하고 나머지의 것은 생략해도 상관없다. 윈도우 통지도 마찬가지이다. 윈도우 통지가 연속적으로 발생하면 수신버퍼에 빈 공간이 늘어난다는 의미이므로 마지막 통지만 전달해도 문제가 발생하지 않는다.

7️⃣ HTTP 응답 메세지를 수신한다.

HTTP 리퀘스트 메세지를 보내면 웹 서버에서 응답 메세지가 돌아오기를 기다리고, 응답 메세지가 돌아오면 그것을 수신한다.

데이터를 수신하면 수신한 데이터 조각과 TCP 헤더의 내용을 조사하여 누락된 데이터가 있는 지 검사하고 문제가 없으면 송신측에 ACK 번호를 반송한다. 그리고 데이터의 조각을 수신 버퍼에 일시 보관하고 데이터 조각을 연결하여 애플리케이션에게 건네준다. 이를 정확하게 설명하자면 수신 버퍼에 저장된 데이터를 애플리케이션 메모리 영역에 옮기고 제어권을 애플리케이션에게 되돌려준다.

4. 서버에서 연결을 끊어 소켓을 말소한다.

1️⃣ 데이터 보내기를 완료했을 때 연결을 끊는다.

데이터의 송 수신 동작이 완료되면 대상과의 연결을 끊는다. 이때 연결 끊기를 시도하는 대상은 서버나 클라이언트의 구분이 없다. 만약 서버에서 먼저 연결을 끊는다고 가정하면 서버측의 프로토콜 스택이 TCP 헤더를 생성하고 컨트롤비트인 FIN의 값을 1로 설정하여 클라이언트에게 전송한다. 이때 서버측은 소켓이 연결 끊기 동작에 들어갔다는 것을 기록한다. 서버에서 FIN을 설정한 TCP 헤더가 클라이언트에게 도착하면 클라이언트는 자신의 소켓에 서버가 연결끊기 작업에 들어갔다는 것을 기록하고 서버측에 FIN 값을 수신했다는 의미로 ACK 값을 전송한다.

클라이언트측 소켓이 애플리케이션에 데이터를 전부 전달하면 클라이언트측의 애플리케이션도 close를 호출하여 데이터 송 수신 동작을 중지한다. 그러면 클라이언트의 프로토콜 스택도 서버와 마찬가지로 TCP 헤더에 FIN 값을 1로 설정하여 서버측에 전송한다. 서버측에서 ACK 값이 되돌아오면 서버와의 대화는 끝이난 것이다.

[연결 끊기 동작]

  1. 클라이언트가 FIN 송신
  2. 서버가 ACK 번호 송신
  3. 서버가 FIN 송신
  4. 클라이언트가 ACK 번호 송신

🌟  이때 연결 종료의 순서는 클라이언트와 서버가 서로 바뀔수도 있다. 

2️⃣ 소켓을 말소한다.

서버와의 대화가 끝나면 더이상 소켓을 사용하여 서버와 대화할 수 없다. 하지만 이때 바로 소켓을 말소시키면 ACK 번호의 응답이 늦어 FIN을 재전송 할 때 기록 정보가 제거되어 어느 소켓이 수신 대상인지 알 수 없게 된다. 뿐만아니라 동일한 포트로 새롭게 생성된 소켓이 FIN을 수신하여 오동작이 발생할 수 있다. 이러한 문제 때문에 소켓을 바로 말소하지 않고 일정시간 기다린 후 말소한다.



Reference

https://velog.io/@anhesu11/HTTP-기본-이론-정리

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

This post is licensed under CC BY 4.0 by the author.