카테고리 없음

TCP에 대해 잘 모르는 그대여 들어와라 !

윤-찬미 2022. 1. 18. 21:36

Transport Layer (4계층)

  • end point 간 신뢰성 있는 데이터를 포트 번호에 해당하는 프로세스에 데이터를 전달한다.
  • 서로 다른 호스트에서 동작하는 애플리케이션 프로세스간의 논리적 통신을 제공한다.트랜스포트가 논리적 통신을 제공해서 두 컴퓨터가 직접 연결된 것처럼 통신하는 기능을 함 → 예를 들어 두개의 다른 컴퓨터에서 각각 카카오톡 프로그램을 실행 시켜 카톡을 한다고 했을 때 트랜스포트가 논리적 통신을 제공해서 두 컴퓨터가 직접 연결된 것처럼 통신하는 기능을 함.

아래는 OSI 7계층 표이다.

 

TCP / UDP

이러한 Transport Layer 는 두개의 주된 프로토콜을 가진다.

바로 연결형 프로토콜 TCP, 비연결형 프로토콜 UDP 이다.

여기서 “주된” 이라는 표현을 썼는데 Transport Layer 에는 TCP, UDP 프로토콜만 있는 것은 아니기 때문이다.

아래는 위키백과에 나와 있는 Transport Layer 의 프로토콜 표 이다.

해당 글에서는 TCP, UDP를 두 파트로 나누어 설명할 예정이고 오늘은 TCP만 다룬다.

 

TCP

전송 제어 프로토콜(Transmission Control Protocol)

포인트는 TCP는 연결형 프로토콜 이라는 것이다. 따라서 아래와 같은 특징을 가지고 있다.

아래서 자세히 설명할 예정이니, 이런게 있구나 정도로만 알아두면 될 것 같다.

  • 신뢰성 있는 데이터 통신을 가능 하게 해주는 프로토콜
  • 데이터의 순차 전송을 보장
  • Flow control (흐름 제어)
  • Congestion Control (혼잡 제어)
  • Error Detection (오류 감지)

 

재밌는 TCP 역사 이야기

TCP를 공부하다보니 문득 TCP는 언제 왜 나왔을까? 에 대한 궁금증이 생겼다.

옛날 옛날에(약 1960년쯔음) 미국 정부기관 DARPA(과거 ARPA) 의 연구원들이 핵 전쟁이나 기타 재난 상황에서도 운용가능한 네트워크를 개발하기 위한 프로젝트를 시작했다.

해당 연구원들은 통신 도중에 적의 공격으로 네트워크의 일부가 공격 당하더라도 데이터 전송이 멈추지 않는 네트워크를 원했다.

 

역사 배경을 잠깐 말하자면 당시 소련이랑 미국이랑 싸우고 있었다. (단어선택이 조금 싸구려 같아도 우선 넘어가자)

당시에는 회선을 독점해 데이터 통신을 진행했는데,(회선 교환) 이러한 방법은 다량의 데이터를 빠르게 보낼 수 있으나,

해당 회선의 연결이 끊어지는 순간 모든 데이터를 잃어버리기에 그들이 최종적으로 원하는 모습은 아니였다.

 

쉬운 예시를 들자면,

이렇게 회선 교환 방식의 경우에는 통신을 하고 싶은 상대방과 물리적으로 회선을 하나 딱 잡아놓고, 계속 통신을 하는 것이다.

우리가 전화를 걸 때 상대방이 통화 중이면 상대방이 통화중이니 다음에 다시 걸어주세요. 어쩌고 나오는 것과 같은 원리이다.

 

패킷을 만들어보자

이러한 네트워크를 위해서는 패킷 통신이 필요했다.

데이터를 하나의 회선을 사용하여 보내다가 해당 회선이 타격을 입으면 전송되던 데이터와도 영원히 빠빠이다😭

따라서 데이터를 잘게 쪼갠 후 여러 개의 회선을 통해 보내자는 것이다.

이러한 방식을 패킷교환 방식이라고 한다.

이 아이디어는 당시 연구원이였던 RAND Corporation 의 Baran 이라는 사람이 정부로 부터 핵 공격을 견뎌내고,

계속 작동할 수 있는 컴퓨터 통신 네트워크에 대한 계획을 세우라는 지시를 받고 생각해 낸 아이디어 이다.

 

ARPANET의 탄생

1969년 10월 29일 패킷 교환 기술의 실용성을 시험하기 위한 네트워크가 구성되었다.

이것이 바로 ARPANET 이고 우리가 모두 알고 있는 World Wide Web 의 시초 라고도 할 수 있겠다.

연구원들이 교수의 지도하에 두대의 컴퓨터로 패킷 교환을 보내고 있다.

1972년 밥칸 이라는 사람이 NCP 라는 프로토콜을 만들었다.

초기 ARPANET 프로젝트에는 해당 프로토콜이 활용되었다.

해당 프로토콜은 열악한 통신 환경에서도 통신이 끊기지 않기 위해 다양한 방법이 고안되어 나온 프로토콜이였다.

 

하지만 초기의 패킷 방식에는 몇가지 고민거리가 있었다.

🤷🏻‍♀️ 보내는 쪽에서 데이터를 쪼개서 보내는데 받는 쪽에서 다시 어떻게 조립하지?(1)

🤷🏻‍♀️ 중간에 패킷 한두개가 손실 되면 어떡하지?(2)

🤷🏻‍♀️ 받는 쪽에서 한번에 처리할 수 없는 양을 보내면 어떡하지?(3)

 

이후 위의 ARPANET에서는 문제들을 어떻게 해결할지 고민하였다.

따라서 아래와 같은 방식들이 고안되었고, 이러한 문제들의 해결책을 담고 있는 프로토콜이 바로 TCP이다.

시퀀스 번호 생성 (데이터의 보낸 순서 대로 숫자를 붙여줌)(1)

ARQ (해당 패킷만 다시 보내줌)(2)

슬라이딩 윈도우 (버퍼 크기를 알려주고 그만큼만 보낼 수 있도록 해준다)(3)

 

이후 몇 개의 군에서만 사용되던 알파넷이 대중들에게 공개되고 ,

전 세계적으로 연결되며 인터넷으로 발전하게 되었고,

덩달아 알파넷의 통신 프로토콜이었던 TCP가 널리 알려지게 되었다.

 

TCP header

헤더에 담겨 있는 정보들을 풀어서 보면 아래와 같다.

  • Source Port : 출발지 포트번호
  • Destination Port : 목적지 포트번호
  • Sequence Number : 순차전송을 할 수 있도록 한다.
  • Acknowledgment Number : 수신하기를 기대하는 다음 byte 번호 (마지막으로 수신에 성공한 번호의 +1)
  • Data Offset : 헤더 길이 필드이며 IPv4와 마찬가지로 나누기 4 계산 후 2진수로 작성됨
  • Reserved : 예약된 필드, 현재 사용되지 않음
  • Window Size : 자신의 수신 버퍼 여유용량 크기를 통보하여 얼마만큼의 데이터를 받을 수 있는지 상대방에게 알려주어 흐름제어를 수행하게 되는 필드
  • TCP Flags: TCP 연결 제어 및 데이터 관리

 

TCP - 3 way handshake 으로 연결한다.

tcp는 맨 처음에 말한 것 처럼 연결형 프로토콜 이다.

연결은 아래와 같이 3 way handshake 을 통해 연결한다.

  1. client 가 server한테 너랑 연결할래! 하고 syn을 보낸다. → 이때 client는 SYN_SENT 상태 server는 Wait for Client 상태가 된다.
  2. server는 syn을 잘 받았다는 의미로 ack를 보내고 나도 너랑 연결할래! 하고 syn을 같이 보낸다. → 이때 server는 client가 ack응답을 하길 기다리는 SYN_RECEIVED 상태가 된다.
  3. client는 잘받았다는 의미로 ack를 보내주고 연결이 이루어 진다.

TCP - 4 way handshake 으로 연결을 끊는다.

 

  1. client가 server한테 연결을 끊자는 FIN을 보낸다. 이때 client는 FIN WAIT 상태가 된다.
  2. server는 잘 받았다는 의미로 ACK를 client 한테 보내고 자신의 통신이 끝날때까지 기다리는데,이때 server는 CLOSE_WAIT 상태가 된다.
  3. 연결 종료 준비가 된 server는 client한테 FIN을 보낸다.
  4. client는 server로 부터 받은 FIN의 응답으로 ACK를 보내고 둘의 연결은 끊어진다.

 

만약 server에서 FIN을 보내기전 보낸 패킷이 라우팅 지연이나 패킷 유실 등으로 인해

FIN 패킷보다 늦게 도착하는 경우 어떻게 할까?

client에서 세션을 종료시킨 후 뒤늦게 도착하는 패킷이 있다면 이 패킷은 Drop되고 데이터는 유실될 것.

따라서 client 이러한 현상에 대비하여 client는 Server로부터 FIN을 수신하더라도 일정시간(디폴트 240초)

동안 세션을 남겨놓고 잉여 패킷을 기다리는 과정을 거치게 되는데 이 과정을 "TIME_WAIT" 라고 한다.

일정시간이 지나면, 세션을 만료하고 연결을 종료시키며, "CLOSE" 상태가 된다.

 

흐름을 제어한다.

흐름을 제어한다? 무슨 말인지 잘 모를 수 있을 것 같다.

흐름제어란, 송신자가 보내는 메세지 속도가 수신자가 메세지를 처리하는 속도보다 빨라 수신자는 처리할 수 있는 버퍼의 크기를 넘어섰을 때 이를 overflow 라고 한다.

이때 수신자가 한번에 처리할 수 있는 양을 위에서(TCP header) 봤던 window size 라고 한다.

아무튼 이러한 오버플로우를 방지하기 위해 송신자와 수신자간의 속도를 맞춰 주는 것을 흐름제어라고 한다.

 

“슬라이딩 윈도우” 로 흐름을 제어한다.

tcp의 흐름제어 방식은 슬라이딩 윈도우 방식을 채택하고 있다.

사전에 해당 슬라이딩 윈도우에 대한 이해도를 높이기 위해 몇가지 알고 가면 좋을 것들을 설명해보도록 하겠다.

ARQ

TCP 하면 생각 나는 단어는 바로 “신뢰” 이다.

TCP는 신뢰적인 데이터 전송을 위해서 자동 재전송 요구[Automatic Repeat reQuest, ARQ]를 수행한다.

ARQ란, 수신자가 패킷을 받으면, 송신자측에서 잘받았어하고 ACK 를 보내거나,

못받았어 하고 NACK 를 보내주는 기능이다.

 

Stop and Wait

가장 직관적이고 기본적인 방식인데,

송신자가 패킷을 1개 보내고 수신자 측에서 잘받았어(ACK) 못받았어(NACK) 할때 까지 기다리는 방식이다.

하지만, 송신측이 ACK을 수신할 때까지 다음 프레임을 전송할 수 없으므로 전송효율이 떨어진다.

심지어, 송신측에서 데이터를 보내봐야 수신측이 처리할 수 있는 상태인지아닌지 알 수 있다.

귀여운 짱구

 

이렇게 Stop and Wait 방식을 사용하면 비효율적인 부분들이 많기에 현재는 슬라이딩 윈도우 기법을 사용한다.

아마 알고리즘 공부를 조금 해보신 분들은 슬라이딩 윈도우 알고리즘을 몇번 풀어보셨을 것 같은데, 바로 그거다.

 

슬라이딩 윈도우 기법

아래와 같은 구조가 있다고 해보자.

참고로 송신 측의 윈도우 크기는 맨 처음 TCP의 연결하는 3 Way Handshake 때 결정된다.

 

데이터 0, 1을 전송했다고 가정하면,

슬라이딩 윈도우의 구조는 다음과 같이 변하며 윈도우의 크기는 전송한 데이터 프레임만큼 줄어들게 된다.

 

이때 만약 수신측에서 ACK라는 프레임을 받게 된다면 전송측은 0, 1이 데이터를 정상적으로 받았음을 알게 되고

전송측은 ACK 프레임에 따른 프레임의 수만큼 오른쪽으로 경계가 확장된다.

이렇게 윈도우를 옆으로 밀면서 가는 방식이기에 이를 슬라이딩 윈도우 라고 부른다.

 

오류를 제어한다.

TCP는 재전송 기반 오류 제어를 사용한다.

즉 ARQ 기법을 사용해 프레임이 손상되었거나 손실되었을 경우, 재전송을 통해 오류를 복구한다.

오류제어 기법 또한 여러가지가 있다.

 

Stop and Wait

눈치가 빠르신 분은 여기서 어? 아까 흐름제어 때 나왔던 거 아닌가? 할 것 같다.

맞다. 같은 기법이다. 이친구도 흐름제어 때 봤던 것 처럼 그대로 데이터를 보내고,

데이터를 제대로 받았다는 ACK응답이 일정시간 없으면 TIME OUT이 발생하며 오류를 감지할 수 있다.

하지만, 흐름제어에 슬라이딩 윈도우를 쓰고 있는데,

오류제어에서 Stop and Wait 를 쓴다면 슬라이딩 윈도우를 쓰는 이점을 잃어버린다.

 

Go Back N

Go Back N 은 패킷을 전송할 때 수신측에서 데이터를 잘못 받은 것이거나 못 받을 경우에 그 패킷 번호부터 다시 재전송을 하는 기법 이다.

 

만약 아래처럼 데이터 전송을 하다가 수신자로 부터 2번 패킷을 못받았는데!! 라고 NACK을 받으면,

 

2번으로 돌아가서 다시 데이터를 전송한다. 말 그대로 n번째로 back 한다.

 

Selective Repeat

Selective Repeat은 말 그대로 선택적인 재전송을 의미한다.

Go Back N 방법도 Stop and Wait에 비하면 많이 효율적인 방법이지만,

에러가 발생하면 그 이후에 정상적으로 전송되었던 데이터까지 모두 폐기 처분되어 다시 전송해야한다는 비효율이 아직 존재한다. 그래서 나온 방식이 에러난 데이터만 재전송해줘 방식인 것이다.

아까처럼 데이터를 전송하다가 2번 데이터가 유실 되었다고 해보자.

 

그러면 쿨하게 2번만 다시 보내준다.

 

어? 그러면 완전 이득? 아닌가 할 수 있는데..

대신 이 방식은 버퍼에 순차적으로 데이터가 쌓이질 않기에 재정렬이 필요하다는 단점이 있다.

따라서 위 방식들의 특징을 잘 고려해서 오류제어를 할 필요 있다.

 

혼잡을 제어한다.

말그대로 혼잡을 제어한다.

근데 뭐가 혼잡하다는 걸까? 사실 우리가 살고 있는 인터넷의 세계

즉 네트워크의 세계는 엄청 나게 많은 것들이 얽히고 얽혀 있다.

만약 한 라우터에 데이터가 몰릴 경우를 가정하면, 자신에게 온 데이터를 모두 처리할 수 없을 수도 있고,

계속된 오류로 또 재전송을 하고 결국 혼잡만 가중 시켜 오버플로우나 데이터 손실을 만든다.

따라서 이러한 네트워크의 혼잡을 피하기 우해 송신측에서 보내는 데이터의 전송속도를 강제로 줄이게 되는데,

이러한 작업을 혼잡제어라고 한다.

즉 흐름 제어가 송·수신 측 사이의 패킷 수를 제어하는 기능이라면,

혼잡 제어는 네트워크 내의 패킷 수를 조절하여 네트워크의 오버플로(overflow)를 방지하는 기능을 한다.

 

여기서도 잠깐 옛날 이야기를 해보자면..
TCP의 혼잡 제어는 1980년대 반 제이콥슨이 도입하였다. 그 당시의 인터넷 환경은 혼잡 붕괴 현상이 큰 문제거리였다.
각 호스트는 정보를 빨리 보내기 위하여 정해진 시간 내에 보낼 수 있는 최대의 패킷을 보냈고,
일부 라우터에서는 혼잡 현상이 발생하여 정해진 시간 내에 받은 패킷들을 모두 처리하지 못하였다.
정해진 시간 내에 패킷이 처리되지 않으면 호스트는 패킷을 재전송하였고, 라우터는 더 많은 패킷을 받게 되어서 혼잡 현상이 더 심해졌다.

 

혼잡 윈도우 크기

MSS는 한 세그먼트에 최대로 보낼 수 있는 데이터의 양을 나타내는 값인데,

대략 다음과 같은 계산을 통해 구할 수 있다.

(MSS = MTU – IP Header의 크기(최소 20byte) – TCP Header의 크기(최소 20byte))

여기서 등장하는 MTU(Maximum Transmission Unit)라는 친구는 한번 통신 때 보낼 수 있는 최대 단위 이다.

 

혼잡을 제어하는 방식

혼잡을 제어하는 방식은 여러가지가 있는데, 대표적으로 아래와 같은 방식들이 있다.

 

AIMD

처음에 패킷을 하나씩 보내고 이것이 문제없이 도착하면 window 크기(단위 시간 내에 보내는 패킷의 수)를

1씩 증가시켜가며 전송하는 방법 이다.

패킷 전송에 실패하거나 일정 시간을 넘으면 패킷의 보내는 속도를 절반으로 줄인다.

식으로 표현하면 아래와 같은 셈이다.

ws + 1
ws * 0.5

AIMD는 window size를 1MSS씩 밖에 증가시키지 않기 때문에,

네트워크의 모든 대역을 활용하여 빠른 속도로 통신하기까지 시간이 오래 걸린다는 단점이 있다.

→ 여러 호스트가 한 네트워크를 공유하고 있으면 나중에 진입하는 쪽이 처음에는 불리하지만 시간이 흐르면 평형 상태로 수렴하게 되는 특징

 

Slow Start (느린 시작)

윈도우의 크기를 1, 2, 4, 8... 과 같이 2배씩 증가시키고(지수 함수 꼴로 증가),

혼잡이 감지되면 윈도우 크기를 1로 줄여 버린다.

시간이 지날수록 AIMD 보다 빠르게 윈도우 크기를 증가시킨다.

처음에는 네트워크의 수용량을 예상할 수 있는 정보가 없지만,

한번 혼잡 현상이 발생하고 나면 네트워크의 수용량을 어느 정도 예상할 수 있으므로 혼잡 현상이 발생하였던

Window size의 절반까지는 이전처럼 지수 함수 꼴로 창 크기를 증가시키고,

그 이후부터는 완만하게 1씩 증가시키는 방식이다

 

Fast Retransmit (빠른 재전송)

빠른 재전송(Fast Retransmit)은 TCP의 혼잡 조절에 추가된 정책이다.

패킷을 받는 쪽에서 먼저 도착해야 할 패킷이 도착하지 않고 다음 패킷이 도착한 경우에도 ACK 패킷을 보낸다.

단, 순서대로 잘 도착한 마지막 패킷의 다음 패킷의 순번을 ACK 패킷에 실어서 보낸다.

따라서 중간에 패킷하나가 손실되게 되면 보내는 측에서는 순번이 중복된 ACK 패킷을 받게 되고,

이것을 감지하는 순간 문제가 되는 순번의 패킷을 재전송해 줄 수 있다

빠른 재전송은 중복된 순번의 패킷을 3개 받으면 재전송을 한다.

그리고 이런 현상이 일어나는 것은 약간 혼잡한 상황이 일어난 것이므로 혼잡을 감지하고, 윈도우 크기를 줄이게 된다.

 

Fast Recovery (빠른 회복)

빠른 회복 정책(Fast Recovery)은 혼잡한 상태가 되면 윈도우 크기를 1로 줄이지 않고 반으로 줄이고,

선형 증가시키는 방법이다. 빠른 회복 정책까지 적용하면 혼잡 상황을 한번 겪고 나서부터는 AIMD 으로 동작한다.

 

여기까지 읽느라 너무 많이 수고 했습니다!

아직 조금 더 정리하고 싶은 내용이 남았는데, 차차 추가하도록 해야겠다.

쓰다보니 내용이 정말 길어졌는데 더 정리하고 싶은 내용은 2부로 이어서 하든 해야겠다.

 

참고

https://www.youtube.com/watch?v=QD3oCelHJ20

https://brunch.co.kr/@swimjiy/35

https://www.venturesquare.net/514020

https://evan-moon.github.io/2019/11/10/header-of-tcp/

 

틀린 내용 지적 및 추가 정보 피드백은 언제나 대환영 입니다