본문 바로가기

네트워크/네트워크 기초

연결지향형 TCP 프로토콜: 신뢰성 있는 데이터 전송의 핵심

이전 장에서 우리는 UDP라는 빠르고 간편하지만 신뢰성은 보장하지 않는 프로토콜을 배웠습니다. 하지만 인터넷 통신의 상당 부분은 데이터가 정확하고 순서대로, 빠짐없이 전달되어야 합니다. 웹 페이지 로딩, 파일 다운로드, 이메일 전송 등을 생각해 보세요. 중간에 데이터가 유실되거나 순서가 뒤바뀌면 심각한 문제가 발생합니다.

 

이러한 신뢰성 있는 데이터 전송을 책임지는 것이 바로 4계층 전송 계층(Transport Layer)의 또 다른 주인공, TCP(Transmission Control Protocol)입니다. TCP는 3계층 네트워크 계층(IP)의 비신뢰적인 '최선형 서비스' 위에 어떻게 신뢰성을 구축하는지, 그 복잡하지만 중요한 메커니즘을 자세히 살펴보겠습니다.


1. TCP란 무엇인가? (Transmission Control Protocol)

  • 연결지향 (Connection-Oriented): TCP의 가장 큰 특징은 데이터를 보내기 전에 반드시 양쪽 프로세스 간에 논리적인 연결(Logical Connection)을 수립한다는 것입니다 (3-Way Handshake). 데이터를 다 보낸 후에는 연결을 해제하는 과정(4-Way Handshake)도 거칩니다. 이는 마치 전화를 걸어 상대방이 받았는지 확인하고 대화를 시작하고, 끝날 때 잘 끊는 것과 유사합니다. 이 연결은 통신하는 동안 양쪽 시스템의 상태 정보를 유지합니다.
  • 신뢰성 있는 데이터 전송 (Reliable Data Transfer): TCP는 IP 계층의 비신뢰적인 서비스(Best-Effort) 위에서 동작하지만, 자체적으로 다양한 메커니즘을 통해 데이터의 유실, 중복, 순서 뒤바뀜을 감지하고 복구합니다. 이를 위해 Sequence Number, Acknowledgement Number, Checksum, 타이머, 재전송 등의 기술을 사용합니다.
  • 순서 보장 (Ordered Data Delivery): 송신 측에서 보낸 데이터 순서대로 수신 측 애플리케이션에 전달하는 것을 보장합니다.
    • 왜 순서 보장이 중요할까? (웹 페이지 로딩 예시): 웹 브라우저가 서버로부터 HTML 파일을 받는다고 가정해 봅시다. 이 큰 HTML 데이터는 TCP에 의해 여러 개의 세그먼트(Segment)로 나뉘어 전송됩니다. 각 세그먼트에는 Sequence Number가 부여되어 원래 데이터의 순서를 나타냅니다. 만약 네트워크 혼잡 등으로 인해 이 세그먼트들이 순서가 뒤바뀌어 도착하면, 수신 측 TCP는 이를 인지하고 버퍼에 잠시 보관하며 순서를 재정렬합니다. 만약 중간 세그먼트가 도착하지 않으면, TCP는 해당 누락된 세그먼트의 재전송을 요청합니다. 웹 브라우저와 같은 애플리케이션은 이렇게 TCP가 순서대로 재조립하여 전달해 준 완전한 데이터 스트림을 받아야만 HTML 내용을 올바르게 해석하고 화면에 표시할 수 있습니다. 따라서 세그먼트 순서가 바뀌는 것은 지연 발생 및 성능 저하로 이어질 수 있습니다.
      • 주의: IPv4의 Fragmentation과 헷갈려 하지 말 것.
  • 흐름 제어 (Flow Control): 수신 측의 데이터 처리 능력(수신 버퍼 크기)을 고려하여 송신 측이 데이터를 보내는 속도를 조절합니다. 수신 버퍼가 넘치지 않도록 방지합니다. (Window Size 필드 사용)
  • 혼잡 제어 (Congestion Control): 네트워크 전체의 혼잡 상황을 감지하고 데이터 전송 속도를 동적으로 조절하여 네트워크 붕괴(Congestion Collapse)를 방지합니다. (TCP는 '착한' 프로토콜로, 네트워크 상황을 배려합니다.)
  • 복잡성과 성능: 이러한 다양한 기능 덕분에 TCP는 UDP보다 훨씬 복잡하고, 헤더 오버헤드(최소 20바이트) 및 제어 메시지 교환으로 인해 전송 속도는 상대적으로 느릴 수 있습니다. 하지만 그 대가로 안정적이고 신뢰성 있는 통신을 제공합니다.

결론: TCP는 신뢰성이 매우 중요한 대부분의 인터넷 애플리케이션(HTTP, HTTPS, FTP, SMTP, SSH 등)에서 사용되는 핵심 프로토콜입니다.

 

[궁금할 수 있는 포인트 🤔 & 답변 ✅]

  • Q: "연결지향이라는 게 정확히 무슨 의미인가요? 물리적으로 선이 연결되는 건가요?"
  • A: 물리적인 연결이 아니라, 양쪽 통신 당사자(프로세스)가 데이터를 주고받기 전에 서로 통신할 준비가 되었음을 확인하고, 통신 상태 정보(예: 시퀀스 번호, 윈도우 크기 등)를 교환하여 논리적인 통신 경로를 설정하는 것을 의미합니다. 이 '연결'은 실제 데이터 전송이 끝날 때까지 양쪽 시스템의 메모리(TCP 제어 블록 - TCB)에 상태 정보로 유지됩니다.
  • Q: "TCP는 신뢰성을 어떻게 보장하나요? IP는 패킷을 잃어버릴 수도 있다면서요?"
  • A: TCP는 여러 메커니즘을 조합하여 신뢰성을 보장합니다. ① Sequence Number로 데이터 순서를 관리하고, ② Acknowledgement (ACK) Number로 수신 확인을 하며, ③ Checksum으로 데이터 오류를 검출하고, ④ 타이머를 이용해 일정 시간 응답이 없으면 ⑤ 재전송을 수행하여 유실된 데이터를 복구합니다.
  • Q: "TCP가 UDP보다 항상 느린가요?"
  • A: 일반적으로 연결 설정/해제 과정, ACK 교환, 각종 제어 메커니즘 때문에 순수 전송 지연 시간은 UDP보다 길 수 있습니다. 하지만 TCP는 네트워크 상황에 맞게 전송 속도를 조절(흐름 제어, 혼잡 제어)하고 유실된 데이터를 재전송하여 결과적으로 안정적인 데이터 처리량(Throughput)을 제공합니다. 반면 UDP는 제어 없이 빠르게 보내다가 네트워크 혼잡을 유발하거나 대량의 패킷 유실로 이어져 오히려 전체적인 애플리케이션 성능이 떨어질 수도 있습니다. 상황에 따라 유불리가 다릅니다.

2. TCP 헤더 구조: 신뢰성을 위한 정보들 (최소 20 Bytes)

TCP 헤더는 신뢰성 있는 통신을 위한 다양한 제어 정보를 담고 있어 UDP보다 복잡합니다.

0                   1                   2                   3
 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|          Source Port          |       Destination Port        |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|                        Sequence Number                        |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|                    Acknowledgement Number                     |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|  Data |           |U|A|P|R|S|F|                               |
| Offset| Reserved  |R|C|S|S|Y|I|            Window             |
|       |           |G|K|H|T|N|N|                               |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|           Checksum            |         Urgent Pointer        |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|                    Options                    |    Padding    |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|                             Data                              |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
  • Source Port / Destination Port (각 2바이트): 출발지/목적지 프로세스(애플리케이션) 식별.
  • Sequence Number (Seq, 4바이트): 세그먼트에 포함된 데이터의 첫 번째 바이트에 대한 순서 번호. TCP는 전체 데이터 스트림을 바이트 단위로 간주하고 각 바이트에 번호를 매깁니다. 연결 설정 시 초기 시퀀스 번호(ISN)는 임의의 값으로 설정됩니다.
  • Acknowledgement Number (Ack, 4바이트): 수신 측이 다음으로 수신하기를 기대하는 바이트의 시퀀스 번호. 즉, "Ack 번호 - 1"까지의 모든 데이터를 오류 없이 순서대로 잘 받았음을 의미합니다. ACK 플래그가 1일 때만 이 필드가 유효합니다.
  • Data Offset (4비트): TCP 헤더의 전체 길이를 4바이트 단위로 나타냅니다. (예: 옵션 없는 기본 헤더 20바이트면 값은 5) 옵션 필드 길이에 따라 헤더 길이가 달라지므로 필요합니다.
  • Reserved (6비트): 미래를 위해 예약된 필드. 현재 사용되지 않으며 0으로 설정.
  • TCP Flags (6비트): TCP 세그먼트의 목적이나 상태를 나타내는 제어 비트.
    • URG: 긴급 데이터 포함 여부 (Urgent Pointer 유효)
    • ACK: 확인 응답 번호(Ack Number) 유효 여부
    • PSH: 수신 측은 데이터를 버퍼링 없이 즉시 애플리케이션에 전달하라는 요청
    • RST: 연결 강제 리셋 (비정상 종료)
    • SYN: 연결 설정 요청 (Sequence Number 동기화 시작)
    • FIN: 연결 종료 요청 (데이터 전송 완료)
  • Window Size (2바이트): 흐름 제어용. 수신 측이 현재 추가로 받을 수 있는 데이터의 양(바이트 단위)을 송신 측에 알림.
  • Checksum (2바이트): 헤더 및 데이터의 오류 검출용.
  • Urgent Pointer (2바이트): URG 플래그 설정 시 긴급 데이터의 위치 정보.
  • Options (가변 길이): 추가 기능 협상 (예: MSS, Window Scale, SACK, Timestamps).

[궁금할 수 있는 포인트 🤔 & 답변 ✅]

  • Q: "Sequence Number와 Acknowledgement Number는 왜 필요한가요? UDP처럼 그냥 보내면 안 되나요?"
  • A: TCP의 핵심 목표인 신뢰성 있는 순차적 데이터 전송을 위해 필수적입니다. Sequence Number는 각 데이터 바이트의 순서를 식별하여 수신 측에서 순서가 뒤바뀐 데이터를 재조립하고 중복 데이터를 제거할 수 있게 합니다. Acknowledgement Number는 수신 측이 어디까지 데이터를 성공적으로 받았는지 송신 측에 알려주어, 송신 측이 유실된 데이터를 파악하고 재전송할 수 있도록 합니다. 이것이 없으면 TCP의 신뢰성 보장이 불가능합니다.
  • Q: "TCP Flags는 어떤 역할을 하나요? 왜 이렇게 여러 종류가 필요한가요?"
  • A: TCP Flags는 TCP 연결의 상태를 제어하고 관리하는 핵심 신호입니다. 연결을 시작(SYN)하고, 확인 응답을 보내고(ACK), 데이터를 즉시 전달 요청하고(PSH), 연결을 비정상 종료하고(RST), 정상 종료(FIN)하는 등 다양한 상황에 맞는 제어 신호를 보내기 위해 각기 다른 플래그가 필요합니다. 이 플래그들의 조합을 통해 TCP는 복잡한 연결 상태를 관리하고 신뢰성 있는 통신을 수행합니다.
  • Q: "Window Size는 흐름 제어를 한다는데, 구체적으로 어떻게 동작하나요?"
  • A: 수신 측은 자신이 현재 데이터를 더 받을 수 있는 버퍼 공간의 크기를 Window Size 필드에 담아 송신 측에 알립니다. 송신 측은 이 값을 보고, 수신 측의 버퍼를 넘치게 하지 않도록 전송량을 조절합니다 (수신 윈도우보다 많은 미확인 데이터를 보내지 않음). 수신 측이 데이터를 처리하여 버퍼 공간이 확보되면, 다음 ACK에 더 큰 Window Size 값을 담아 보내 송신 측이 더 많은 데이터를 보낼 수 있도록 동적으로 조절됩니다.

3. TCP 연결 수립 과정: 3-Way Handshake

TCP 통신은 데이터를 보내기 전에 반드시 '3-Way Handshake'라는 과정을 통해 논리적인 연결을 수립합니다.

  • 왜 3-Way Handshake가 필요할까?
    • 양쪽 통신 준비 확인: 클라이언트와 서버 양쪽 모두 데이터를 주고받을 준비가 되었는지, 서로에게 도달 가능한지 확인합니다.
    • 초기 시퀀스 번호(ISN) 교환 및 동기화: 양측이 데이터 순서를 추적하기 위한 시작 번호(ISN)를 서로에게 알리고 합의합니다. ISN은 보안 및 이전 연결과의 혼동 방지를 위해 임의의 값으로 설정됩니다.
    • 통신 파라미터 교환 (옵션): 필요하다면 TCP 옵션을 통해 MSS, 윈도우 스케일 등의 파라미터를 교환하여 효율적인 통신 환경을 설정합니다.
    • Half-Open 상태 방지: 만약 2-Way로 끝낸다면(SYN -> SYN-ACK), 서버는 클라이언트가 자신의 SYN-ACK을 받았는지 확인할 수 없습니다. 클라이언트는 연결 실패로 간주했는데 서버 혼자 연결되었다고 생각하는 '반만 열린(Half-Open)' 상태가 될 수 있습니다. 3-Way는 클라이언트의 마지막 ACK를 통해 양측 모두 연결 수립을 확인하게 해줍니다.
  • 과정:
    1. [SYN] 클라이언트 → 서버 (연결 요청): 클라이언트가 서버에 연결을 요청하며 SYN 플래그를 1로 설정하고, 자신의 ISN x를 Sequence Number에 담아 보냅니다. (상태: 클라이언트 SYN_SENT, 서버 LISTEN)
    2. [SYN-ACK] 서버 → 클라이언트 (요청 수락 및 확인): 서버는 SYN 요청을 받고 연결을 수락합니다. SYN과 ACK 플래그를 1로 설정하고, 자신의 ISN y를 Sequence Number에 담습니다. 또한 클라이언트의 ISN x를 잘 받았다는 의미로 Acknowledgement Number에 x+1을 담아 응답합니다. (상태: 클라이언트 SYN_SENT, 서버 SYN_RECEIVED)
    3. [ACK] 클라이언트 → 서버 (최종 확인): 클라이언트는 서버의 SYN-ACK 응답을 받고, 서버의 ISN y를 인지했음을 알리기 위해 ACK 플래그를 1로 설정합니다. Sequence Number는 x+1, Acknowledgement Number는 y+1을 담아 서버에 보냅니다. 이 마지막 ACK가 서버에 도착하면 양측 모두 연결이 수립(Established)됩니다. (상태: 양측 ESTABLISHED)

  • Sequence Number와 Acknowledgement Number의 의미:
    • SYN 또는 FIN 플래그가 설정된 세그먼트는 실제 데이터가 없어도 시퀀스 번호 공간에서 1바이트를 차지하는 것으로 간주됩니다.
    • ACK 번호 N은 "나는 N-1번 바이트까지의 데이터를 모두 잘 받았고, 다음에는 N번 바이트 데이터를 기다리고 있다"는 의미입니다.

4. TCP 데이터 전송 과정

연결 수립 후, Sequence Number와 Acknowledgement Number를 이용하여 데이터를 신뢰성 있게 주고받습니다.

  • 예시: 클라이언트 "Hello" (5바이트) 전송, 서버 "Hi" (2바이트) 응답 (3-Way Handshake 후, Client Seq=1001, Server Seq=5001부터 시작 가정)
    • 핵심: 각 세그먼트의 Seq 번호는 해당 세그먼트 데이터의 첫 번째 바이트 번호입니다. Ack 번호는 상대방으로부터 마지막으로 성공적으로 수신한 바이트의 다음 번호입니다. 이를 통해 양측은 데이터의 흐름을 추적하고 유실 여부를 판단합니다.

 


5. [심화] TCP 세그먼테이션 vs. IP 프래그먼테이션

웹 페이지 데이터가 '나뉘어 전송된다'고 할 때, 이는 주로 TCP 세그먼테이션(Segmentation)을 의미합니다. IP 프래그먼테이션과는 다른 개념이며, 혼동하지 않는 것이 중요합니다.

  • TCP 세그먼테이션 (4계층): 애플리케이션 데이터를 TCP가 MSS(Maximum Segment Size) 단위로 나누는 정상적이고 일반적인 과정. TCP가 순서/재조립을 책임짐.
  • IP 프래그먼테이션 (3계층): IP 패킷이 경로상 MTU(Maximum Transmission Unit)보다 커서 라우터 등에 의해 어쩔 수 없이 쪼개지는 현상. 최종 목적지 IP 계층에서 재조립. 비효율적이므로 가급적 피해야 함.

결론: 웹 통신에서 데이터 분할은 대부분 TCP 세그먼테이션이며, 이는 TCP 신뢰성 메커니즘의 일부입니다.


6. TCP 상태 전이도 (TCP State Transition Diagram)

TCP 연결은 생성부터 종료까지 여러 가지 상태(State)를 거치며 변화합니다.

출처: https://en.wikipedia.org/wiki/File:Tcp_state_diagram.png

  • 주요 상태 및 전환 트리거 (간략화):
    • CLOSED: 초기 상태.
    • LISTEN: 서버, listen() 호출 후 클라이언트 SYN 대기.
    • SYN_SENT: 클라이언트, connect() 호출 후 SYN 보내고 SYN-ACK 대기.
    • SYN_RECEIVED: 서버, SYN 수신 후 SYN-ACK 보내고 ACK 대기.
    • ESTABLISHED: 양측, 3-Way Handshake 완료 후 데이터 교환 가능.
    • FIN_WAIT_1: close() 호출 후 FIN 보내고 ACK 또는 FIN 대기.
    • FIN_WAIT_2: FIN에 대한 ACK 수신, 상대방 FIN 대기.
    • CLOSE_WAIT: 상대방 FIN 수신 후 ACK 보냄, 애플리케이션 close() 대기.
    • LAST_ACK: CLOSE_WAIT 상태에서 close() 후 FIN 보내고 마지막 ACK 대기.
    • TIME_WAIT: 먼저 close() 요청 후 모든 과정 완료, 일정 시간(2MSL) 대기. (안전한 종료 보장)
    • CLOSING: 양측 동시 close() 시 드물게 발생.
  • netstat 또는 ss 명령어로 현재 연결 상태 확인 가능.

[궁금할 수 있는 포인트 🤔 & 답변 ✅]

  • Q: "3-Way Handshake에서 초기 시퀀스 번호(ISN)는 왜 임의의 값으로 설정하나요? 0부터 시작하면 안 되나요?"
  • A: 보안상의 이유(Sequence Number 예측 공격 방지)와 이전 연결과의 혼동 방지를 위해 임의의 값을 사용합니다.
  • Q: "데이터 전송 시 ACK는 매번 데이터 패킷마다 보내야 하나요?"
  • A: 아닙니다. 효율성을 위해 TCP는 여러 데이터를 받고 하나의 ACK로 응답하거나(Cumulative ACK), 데이터를 보낼 때 이전 수신 데이터에 대한 ACK를 함께 보내는(Piggybacking) 지연된 ACK(Delayed ACK) 방식을 사용합니다.
  • Q: "연결 종료 시 TIME_WAIT 상태는 왜 필요한가요? 그냥 바로 CLOSED 상태로 가면 안 되나요?"
  • A: 두 가지 이유: ① 네트워크에 남아있을 수 있는 지연된 패킷이 다음 연결에 영향을 주는 것을 방지하고, ② 자신이 보낸 마지막 ACK가 유실되었을 경우 상대방이 FIN을 재전송하면 다시 ACK를 보내주어 정상 종료를 보장하기 위함입니다.

[마무리하며]

TCP는 연결 수립, 데이터 전송(순서/오류/흐름/혼잡 제어), 연결 종료 등 UDP보다 훨씬 정교하고 복잡한 메커니즘을 통해 신뢰성 있는 데이터 전송을 보장합니다. 헤더의 각 필드와 플래그, 그리고 정의된 상태 변화는 이 목표를 달성하기 위한 핵심 요소입니다. 비록 약간의 성능 희생이 따르지만, TCP가 제공하는 안정성과 신뢰성은 오늘날 인터넷을 가능하게 하는 가장 중요한 기반 기술 중 하나입니다.