TCP란? (3-Way handshake, 4-Way hanshake)

TCP는 인터넷 상에서 데이터 전송의 신뢰성을 위해 사용되는 전송 계층(Transport Layer)의 프로토콜이다. 클라이언트와 서버 간의 통신에 자주 사용되는 HTTP도 TCP 기반으로 동작한다.

TCP는 신뢰성 있는 데이터 전송을 보장하기 위해 수 많은 기능들을 제공하는데, 이번 글에서는 TCP가 어떤 방식으로 동작하는지 흐름을 따라 정리 해보고자한다.

신뢰성 보장을 위한 조건

신뢰성을 보장하기 위해서는 전송 과정 중 데이터 패킷이 변형되지 않아야 하고, 유실되는 것이 없어야 한다.
문제는 원격에 존재하는 두 호스트 간 통신에서는 이러한 신뢰성을 온전히 보장할 수 없다는 점이다.

Alt Image
TCP가 작동하는 전송계층에서는 응용 계층의 프로세스로부터 소켓을 통해 메시지를 전달 받고, TCP 헤더와 함께 Segment를 생성한다.

각 세그먼트는 패킷으로 다시 캡슐화 되고, 네트워크 경로를 통해 원격의 호스트로 전달된다.

문제는 여러 라우터들을 거치면서 발생하는데, 경로를 통과하면서 데이터 패킷이 유실 되거나 에러가 발생 할 수 있다.

TCP는 피드백(ACKs), 재전송(Retransmission) 메커니즘으로 이 문제들을 해결한다. 구체적인 TCP 데이터 송수신 방식을 알아 보기전에 두 메커니즘에 대해 먼저 살펴보자.

Feedback (ACK)

TCP는 데이터 패킷을 주고 받는 과정에서 수신자는 결과를 피드백(ACK) 한다. 는 원칙을 따른다.

ACK 는 Acknowledgment의 약어로, TCP 통신에서 데이터 패킷을 성공적으로 받았음을 송신 측에 알리기 위해 사용되는 신호이다.

Alt Image
구체적인 송수신 과정은 다음과 같다.

  1. 송신자(A)는 원격지 수신자(B)에게 데이터 패킷을 전송한다.
  2. 수신자(B)는 데이터를 수신한 뒤, 그 결과를 A에게 ACK (Acknowledgement)
  3. 송신자(A)는 수신자(B)의 피드백(ACK)을 기다리고, ACK를 수신한 이후 데이터 패킷을 추가 전송한다.

Retransmission

앞서 ACK가 무엇인지 알아보았다. 수신자는 데이터 패킷에 에러가 있다고 판단하거나(Checksum 검증 실패),
유실되어 전달조차 받지 못한 경우 송신자에게 ACK를 전달하지 않는다.

만약 송신자는 ACK를 받지 못한다면 어떻게 동작할까?

TCP는 일정시간(time out) 동안 수신자의 피드백을 받지 못하면 패킷을 재전송 하는 방식으로 동작한다.

Alt Image Alt Image

위와 같이 수신자의 피드백(ACK)송신자의 재전송 방식 두 가지 방법을 사용하면 데이터 패킷의 누락, 오류 검출은 가능하다. 하지만 두가지 방법으로 모든 문제 상황을 완전히 해결 하는 것은 불가능하다.

만약 수신자의 ACK가 유실된다면 어떻게 될까?

Alt Image
처리 과정을 설명하면 다음과 같다.

  1. 송신자(A)는 데이터를 원격의 수신자(B)에게 전달한다.
  2. 수신자(B)는 ACK를 송신자(A)에게 전송하지만 전달되지 못한다.
  3. 송신자(A)는 수신자(B)로부터 ACK를 전달받지 못하고, 일정 시간 이후 재전송을 시도한다.

송신자는 3. 에서 데이터를 재전송하게 되는데 문제는 수신자가 이미 한차례 수신했던 중복 데이터라는 점이다.

수신자는 (비록 같은 데이터일지라도) 송신자에게 전달 받은 데이터가 중복인지 아닌지 판단할 수 있는 방법이 없다. 즉, 중복 이라는 문제가 발생하는 것이다.

TCP는 Seqeunce Number를 사용하여 이 중복 문제를 해결한다.

Sequence Number

Sequence Number 는 TCP 세그먼트에 순서를 부여하는 역할을 한다. 아래 예시를 보자.

Alt Image

송신자(A)는 각 세그먼트마다 시퀀스 번호를 부여한다.
예를 들어 세그먼트에 N번을 넘버링(SEQ=N)하여 수신측에 전달하면, 수신자(B)는 마지막으로 수신한 시퀀스 번호(N) + 1을 ACK에 첨부한다. (이는 설명의 편의를 위해 구성한 예시로, 수신한 데이터 바이트 수까지 고려해야 하는 TCP의 넘버링 방식과는 차이가 있다. 실제 TCP의 SEQ, ACK 넘버링 방식은 조금 뒷 부분에서 다시 설명한다.)

그럼 이제 시퀀스 넘버링 방식으로 앞서 발생했던 중복 문제를 해결해보자.

Alt Image

  1. 송신자(A)는 SEQ(0)을 세그먼트에 부여하여 수신자(B)에게 전달한다.
  2. 수신자(B)는 데이터 수신 후 시퀀스 번호에 1을 더하여(0 + 1) ACK하지만, 전송 과정에서 유실된다.
  3. 송신자(A)는 일정 시간(time out) 후 재전송을 시도한다.
  4. 수신자(B)는 SEQ=1에 해당하는 데이터를 수신하지 못하였기 때문에, 마지막으로 전달 받았던 SEQ(0) 값 에 1을 더한 ACK(1)을 전송한다.
  5. 송신자(A)는 수신자(B)가 요청한 SEQ(1) 에 해당하는 세그먼트를 수신자(B)에게 전달한다.

Cumulative ACK

시퀀스 넘버링 방식은 송신자의 중복 전송 방지 기능 뿐만 아니라, 다중 전송이 가능하게 한다.

TCP에서 수신자는 송신자에게 전달 받은 마지막 시퀀스 번호에 1 을 더한 결과를 ACK한다. 이러한 동작 방식 덕분에, 송신자는 수신자의 ACK를 기다릴 필요가 없이 연이어 데이터를 전송할 수 있다.

Alt Image


TCP에서 수신자는 송신자의 모든 패킷에 ACK를 하지 않고, 누적한 후 마지막 결과에 대한 ACK를 한다.

ACK를 처리하는 방식에 대해 아래 몇 가지 예외 시나리오를 보면서 더 깊이 이해해보자.

1) ACK 패킷 유실

Alt Image

  1. 송신자(A)는 SEQ=100을 수신자(B)에게 전달한다.
  2. 수신자(B)는 ACK를 송신자(A)에게 전송하지만 유실된다.
  3. 송신자(A)는 SEQ=100의 ACK를 기다리고, 일정 시간이 지나면 재전송한다.
  4. 수신자(B)는 SEQ=100을 이미 전달 받았기 때문에, ACK=101을 전송한다.
  5. 송신자(A)는 ACK로 전달받은 101번 SEQ에 해당하는 세그먼트를 패킷으로 전송한다.

2) ACK 패킷 송신 지연

Alt Image

  1. 송신자(A)는 SEQ(100)을 수신자(B)에게 전송한다.
  2. 송신자(A)는 SEQ(101)을 연이어 수신자(B)에게 전송한다.
  3. 수신자(B)는 송신자(A)의 SEQ(101)을 전달받기 직전에 ACK(101)을 전송한다.
  4. 송신자(A)는 ACK(101)을 전달받고, 마지막으로 전달한 SEQ(101)와 관계 없이 SEQ(100)을 다시 전달한다.
  5. 수신자(B)는 SEQ(101)에 대한 응답으로 ACK(102)을 전송한다.
  6. 송신자(A)는 SEQ(102)를 수신자(B)에게 전송한다.

3) ACK 패킷 유실 후 다음 패킷 ACK

Alt Image

  1. 송신자(A)는 SEQ(100)을 수신자(B)에게 전송한다.
  2. 송신자(A)는 SEQ(101)을 수신자(B)에게 전송한다.
  3. 송신자(A)는 SEQ(102)을 수신자(B)에게 전송한다.
  4. 수신자(B)는 마지막으로 전달 받은 SEQ(101)을 ACK하지만, 유실된다.
  5. 수신자(B)는 SEQ(102)을 전달 받고, 바로 이전 ACK와 관계 없이 ACK(103)을 전송한다
  6. 송신자(A)는 ACK로 전달받은 103번 SEQ에 해당하는 세그먼트를 수신자(B)에게 전송한다.

Window

송신자가 ACK를 기다리지 않고 다중 전송 동작이 가능한 이유는, 통신 과정 중 내부에서 송수신 버퍼로 상태를 관리하기 때문이다.

Alt Image

하지만 무한정으로 데이터를 계속 전송할 수는 없다. 수신자의 데이터 수신 능력이 고려되어야 한다.
수신자를 고려하지 않고 패킷을 전송하다가 수신측의 오류로 유실 된다면, 커넥션 성립과 같은 추가 비용을 수반하는 TCP 통신을 할 이유가 없을 것이다.

TCP에서는 전송할 수 있는 최대 세그먼트 수를 결정하기 위해 Window 방식을 사용한다. 윈도우의 크기는 송수신 과정에서 네트워크 상태에 따라 동적으로 조절되며, 초기 값은 TCP 커넥션 성립 과정에서 결정된다.

TCP 송수신 방식

지금까지 TCP가 어떤 방식으로 신뢰성을 확보하는지 전송 방식을 통해 살펴보았다.
앞선 예제에서는 쉬운 그림을 통한 이해를 위해 1. TCP 송수신 주체, 2.TCP ACK 넘버링 방식 두 가지 내용을 일부 배제하였다. 지금부터는 관련 내용을 다시 정리해보고자 한다.

TCP Sender, Receiver

TCP 통신에서는 송수신자가 뚜렷하게 나뉘지 않는다.
패킷을 일방적으로 보내기만 하는 송신자, 받기만하는 수신자가 명확히 나누어지지 않는 것이다.
송신자 또한 수신자가 될 수 있고, 수신자도 송신자가 될 수 있다.

HTTP Client - Server 통신을 생각해보자.
HTTP 요청 시 클라이언트는 송신자되고, 서버는 수신자가 된다. 이후 서버의 응답 시점에 클라이언트는 수신자가 되고, 서버는 송신자가 되는 것이다.

즉, 통신 과정에서 양측의 송수신자 모두가 SEQ, ACK 번호를 주고 받을 수 있다.

TCP ACK

실제 TCP의 ACK에 부여되는 번호는 현재 수신한 데이터의 크기를 고려해야한다.
ACK 넘버링은 SEQ + 데이터 크기 + 1 규칙을 따른다. 그림을 통해 이해해보자.

Alt Image

  1. A는 B로부터 전달받은 마지막 ACK 번호 111을 SEQ로 지정한다.
    ACK는 B가 전달한 SEQ 값인 200에 바이트 크기 20을 더하고, 추가로 1을 더한다. (200 + 20 + 1 = 221)

    • SEQ : 111
    • ACK : 221
  2. B는 A로부터 전달받은 ACK 값 221을 SEQ로 지정한다. ACK 값으로 A가 전달한 SEQ 값인 111에 바이트 크기 10을 더하고, 추가로 1을 더한다. (111 + 10 + 1)

    • SEQ : 221
    • ACK : 122

TCP Header

네트워크 모델에서의 특정 계층을 잘 이해하려면 계층의 Header를 잘 파악하면 된다.
TCP는 전송계층에 해당되고, 신뢰성 보장을 위해 지원되는 다양한 기능들에 대한 값을 포함하고 있다.

Alt Image

헤더의 구성에는 다음과 같은 값들이 있다.

Alt Image

  • Source Port: 송신자의 포트 번호.
  • Destination Port: 수신자의 포트 번호.
  • Sequence Number: 데이터의 순서를 지정하는 번호로, 어느 데이터 부분부터 전송하는지를 나타낸다.
  • Acknowledgment Number: 다음에 받기를 기대하는 데이터의 첫 시퀀스 번호
  • Data Offset (Header Length): TCP 헤더의 길이를 나타내며, 데이터가 시작되는 위치를 지정한다.
  • Reserved(Not Used): 미래에 필요 시 값을 저장할 임시 공간 현재는 사용되지 않는다.
  • Flags: 통신의 특정 상태나 요청을 나타내는 플래그

    • URG (U - Urgent): 긴급 데이터 전송 여부를 나타낸다. (자주 사용되지 않는다.)
    • ACK (A - Acknowledgment): 이전에 받은 패킷에 대한 응답일 시 1Bit로 설정된다.
    • PSH (P - Push): 데이터를 가능한 빨리 상대방에게 전달하도록 요청한다.
    • RST (R - Reset): 연결을 초기화하거나 거부하는 데 사용된다.
    • SYN (S - Synchronize): 3-Way handshake 설정 시 시퀀스 번호를 동기화하는 데 사용한다.
    • FIN (F - Finish): 데이터 전송이 끝났음을 알리고 연결 종료를 요청한다. (4-Way handshake 시 사용된다.)
  • Window Size: 수신자가 현재 받을 수 있는 데이터의 양을 나타낸다.
  • Checksum: 패킷의 오류를 검사하는 데 사용된다.
  • Urgent Pointer: 긴급 데이터를 처리할 위치를 나타낸니다. URG(U) 플래그가 설정 될 때 유효하다.
  • Options: 선택적으로 사용되며, 추가 기능을 제공할 수 있는 영역이다. (MSS, Window Size등을 설정하는데에 사용한다.)


TCP 3-Way handshake

TCP는 신뢰성을 보장하기 위한 연결지향형 프로토콜이다. 신뢰성 확보를 위해 에러를 탐지하고, 순서를 보장하고, 데이터 유실을 방지한다.

TCP에서는 종단간 안전한 통신을 위해서 커넥션을 성립하는 과정이 선행된다.
TCP 3-way handshake는 이러한 안전한 연결을 수립하는 과정이다.

안전한 통신을 위해서는 서로가 얼마만큼의 데이터를 송수신하고 처리할 수 있는지 알아야 한다.

이러한 목적을 달성하기 위해 TCP 3-way handhsake 과정 중 윈도우 크기(Window Size)와 최대 세그먼트 크기인 MMS(Maximum Segment Size)를 정한다.

윈도우 크기는 흐름 제어에 중요한 역할을 하며, MSS는 네트워크를 통해 전송할 수 있는 최대 데이터 양을 결정한다.

3-way handshake는 다음의 절차로 성립된다.

Alt Image

  1. 최초 송신자(A) 측에서 SYN 패킷(Header SYN Bit 값을 1로 설정)을 수신자(B)에게 전송한다.
    이 때, 최초의 SEQ 번호는 랜덤으로 부여한다.
  2. 수신자(B)는 송신자(A)의 SEQ 값에 대한 ACK 값과 랜덤으로 부여한 최초 SEQ 번호를 함께 전송한다.
  3. 송신자(A)는 수신자(B)로부터 수신한 SEQ 번호를 해야한다.


TCP 4-Way handshake

원격 호스트 간의 모든 TCP 통신이 마무리되면 커넥션을 종료하는 과정을 거치는데,
이 과정을 TCP 4-Way handshake라 한다.

TCP는 데이터 통신 종료 시에도 신뢰성을 추구한다는 목적이 드러난다.

데이터 전송 완료를 보장하고, 패킷의 유실 없이 모두 목적지의 도착하는 것을 확인 한 뒤 안전하고 정확하게 연결을 종료한다.

4-way handshake는 다음의 절차로 수행된다.

Alt Image

  1. A 는 B에게 FIN Bit를 1로 설정한 FIN 패킷을 전송하여 데이터 전송이 끝났음을 알린다.
    이는 더 이상 전송할 데이터가 없음을 의미한다. A는 이 시점부터 FIN-WAIT-1 상태로 들어간다.
  2. 수신자(B)는 A의 FIN 패킷을 받고 ACK 패킷을 전송한다. B는 FIN 패킷을 수신한 시점부터 모든 남은 데이터를 전송할 수 있는 CLOSE-WAIT 상태에 진입하고, 송신자(A)는 ACK를 수신 후 FIN-WAIT-2 상태로 전환한다.
  3. 수신자(B)는 모든 데이터 전송을 마치고 A에게 FIN 패킷을 전송하여 연결 종료를 요청한다.
    B는 이 시점부터 LAST-ACK 상태로 전환한다.
  4. 송신자(A)는 수신자(B)의 FIN 패킷을 수신한 뒤, 마지막 ACK 패킷을 수신자(B)로 전송한다.
    A는 이 FIN 패킷 수신 시점부터 TIME-WAIT 상태로 들어가 일정 시간 동안 기다린 후 연결을 완전히 종료한다.

송신자(A)가 ACK 이후 연결을 즉시 종료 하지 않는 이유는?

A는 B의 FIN 패킷을 ACK 후에 즉시 종료하지 않는다. 이는 네트워크 지연으로 인해 발생할 수 있는 패킷의 재전송을 처리하기 위해 일정 시간을 두는 것이다. B는 마지막 ACK를 받은 후 연결을 완전히 종료한다.

정리

지금까지 TCP의 종단 간 통신의 신뢰성을 확보하기 위한 동작 방식, 연결과 종료까지의 과정들을 알아보았다. TCP는 정말 많은 기능을 제공하고, 글 하나로 모든 것을 정리할 수 있을만큼 단순한 프로토콜이 아니다.

본 글에서 다룬 것들 이외에도 네트워크의 혼잡 상황을 고려한 Congestion Control 같은 내용과, Window Size에 따른 구체적인 동작, 타임아웃 동작 방식 등 아주 세세한 내용까지는 다루지 못하였다.

이는 추후 별도 포스트를 통해 자세히 다뤄보고자 한다.


References