TCP의 구조
TCP의 구조
1. TCP란?
TCP 개념
┌─────────────────────────────────────────────────────────────┐
│ TCP (Transmission Control Protocol) │
├─────────────────────────────────────────────────────────────┤
│ │
│ 정의: │
│ • 전송 계층에서 동작하는 연결 지향형 프로토콜 │
│ • 신뢰성 있는 데이터 전송을 보장 │
│ • 순서 보장, 오류 검출, 재전송 기능 제공 │
│ │
│ 핵심 특징: │
│ ───────────────────────────────────────── │
│ • 연결 지향 (Connection-Oriented) │
│ • 양방향 통신 (Full-Duplex) │
│ • 바이트 스트림 (Byte Stream) 전송 │
│ • 흐름 제어 (Flow Control) │
│ • 혼잡 제어 (Congestion Control) │
│ │
└─────────────────────────────────────────────────────────────┘TCP 세그먼트
TCP 헤더가 붙은 데이터를 세그먼트(Segment)라고 한다:
┌─────────────────────────────────────────────────────────────┐
│ │
│ [애플리케이션 계층] │
│ │ │
│ ↓ 데이터 │
│ ┌─────────────────────────────────────────────┐ │
│ │ 데이터 │ │
│ └─────────────────────────────────────────────┘ │
│ │ │
│ ↓ TCP 헤더 추가 (전송 계층) │
│ ┌───────────┬─────────────────────────────────┐ │
│ │ TCP 헤더 │ 데이터 │ │
│ │(20~60 bytes)│ │ │
│ └───────────┴─────────────────────────────────┘ │
│ ↑ │
│ └──────── TCP 세그먼트 ───────────────── │
│ │
│ ↓ IP 헤더 추가 (네트워크 계층) │
│ ┌─────────┬───────────┬───────────────────────┐ │
│ │ IP 헤더 │ TCP 헤더 │ 데이터 │ │
│ └─────────┴───────────┴───────────────────────┘ │
│ ↑ │
│ └──────── IP 패킷 ────────────────────── │
│ │
└─────────────────────────────────────────────────────────────┘2. TCP 헤더 구조
TCP 헤더 상세
┌─────────────────────────────────────────────────────────────┐
│ TCP 헤더 구조 (20~60 바이트) │
├─────────────────────────────────────────────────────────────┤
│ │
│ 비트: 0 16 31 │
│ ├─────────────────┼─────────────────┤ │
│ 0 │ 출발지 포트 │ 목적지 포트 │ │
│ │ (16비트) │ (16비트) │ │
│ ├─────────────────┴─────────────────┤ │
│ 32 │ 순서 번호 (Sequence Number)│ │
│ │ (32비트) │ │
│ ├───────────────────────────────────┤ │
│ 64 │ 확인 응답 번호 (ACK Number) │ │
│ │ (32비트) │ │
│ ├────┬──────┬────┬──────────────────┤ │
│ 96 │헤더│예약 │제어│ 윈도우 크기 │ │
│ │길이│(6bit)│비트│ (16비트) │ │
│ │(4) │ │(6) │ │ │
│ ├────┴──────┴────┼──────────────────┤ │
│ 128 │ 체크섬 (16비트)│ 긴급 포인터(16비트)│ │
│ ├────────────────┴──────────────────┤ │
│ 160 │ 옵션 (0~40 바이트) │ │
│ ├───────────────────────────────────┤ │
│ │ 데이터 │ │
│ │ ... │ │
│ └───────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────┘각 필드 상세 설명
┌─────────────────────────────────────────────────────────────┐
│ TCP 헤더 필드 설명 │
├─────────────────────────────────────────────────────────────┤
│ │
│ 1. 출발지 포트 번호 (Source Port) - 16비트 │
│ ───────────────────────────────────────── │
│ • 송신 애플리케이션의 포트 번호 │
│ • 범위: 0 ~ 65535 │
│ • 예: 52341 (클라이언트의 임시 포트) │
│ │
│ 2. 목적지 포트 번호 (Destination Port) - 16비트 │
│ ───────────────────────────────────────── │
│ • 수신 애플리케이션의 포트 번호 │
│ • 예: 80 (HTTP), 443 (HTTPS), 22 (SSH) │
│ │
│ 3. 순서 번호 (Sequence Number) - 32비트 │
│ ───────────────────────────────────────── │
│ • 전송하는 데이터의 첫 번째 바이트 번호 │
│ • 초기값: 랜덤 (ISN: Initial Sequence Number) │
│ • 순서 보장 및 재조립에 사용 │
│ │
│ 4. 확인 응답 번호 (ACK Number) - 32비트 │
│ ───────────────────────────────────────── │
│ • 다음에 받기 기대하는 바이트 번호 │
│ • "여기까지 잘 받았으니, 다음 번호 보내줘" │
│ • ACK 플래그가 1일 때만 유효 │
│ │
│ 5. 헤더 길이 (Data Offset) - 4비트 │
│ ───────────────────────────────────────── │
│ • TCP 헤더의 길이를 4바이트 단위로 표시 │
│ • 최소 5 (20바이트), 최대 15 (60바이트) │
│ • 옵션 포함 여부에 따라 달라짐 │
│ │
│ 6. 예약 (Reserved) - 6비트 │
│ ───────────────────────────────────────── │
│ • 미래를 위해 예약된 필드 │
│ • 항상 0으로 설정 │
│ │
│ 7. 제어 비트 (Control Flags) - 6비트 │
│ ───────────────────────────────────────── │
│ • 연결 제어 정보를 담는 플래그 │
│ • URG, ACK, PSH, RST, SYN, FIN │
│ (아래에서 상세 설명) │
│ │
│ 8. 윈도우 크기 (Window Size) - 16비트 │
│ ───────────────────────────────────────── │
│ • 수신 가능한 데이터 크기 (바이트) │
│ • 흐름 제어에 사용 │
│ • 최대 65535 바이트 (옵션으로 확장 가능) │
│ │
│ 9. 체크섬 (Checksum) - 16비트 │
│ ───────────────────────────────────────── │
│ • 헤더와 데이터의 오류 검출용 │
│ • 의사 헤더(Pseudo Header) 포함하여 계산 │
│ │
│ 10. 긴급 포인터 (Urgent Pointer) - 16비트 │
│ ───────────────────────────────────────── │
│ • URG 플래그가 1일 때 유효 │
│ • 긴급 데이터의 마지막 위치 │
│ │
│ 11. 옵션 (Options) - 가변 (0~40 바이트) │
│ ───────────────────────────────────────── │
│ • MSS, 윈도우 스케일, 타임스탬프 등 │
│ • 4바이트 단위로 패딩 │
│ │
└─────────────────────────────────────────────────────────────┘3. 제어 비트 (Control Flags)
6개의 제어 비트
┌─────────────────────────────────────────────────────────────┐
│ 제어 비트 (6비트) │
├─────────────────────────────────────────────────────────────┤
│ │
│ 비트 위치: │
│ ┌─────┬─────┬─────┬─────┬─────┬─────┐ │
│ │ URG │ ACK │ PSH │ RST │ SYN │ FIN │ │
│ └─────┴─────┴─────┴─────┴─────┴─────┘ │
│ 5 4 3 2 1 0 │
│ │
│ 초깃값: 모두 0 │
│ 활성화: 해당 비트가 1이 됨 │
│ │
└─────────────────────────────────────────────────────────────┘각 플래그의 역할
┌─────────────────────────────────────────────────────────────┐
│ 제어 비트 상세 설명 │
├─────────────────────────────────────────────────────────────┤
│ │
│ URG (Urgent) - 긴급 비트 │
│ ───────────────────────────────────────── │
│ • 긴급 데이터 포함 표시 │
│ • 긴급 포인터 필드와 함께 사용 │
│ • 수신측은 다른 데이터보다 먼저 처리 │
│ • 예: Ctrl+C로 원격 프로그램 종료 │
│ │
│ ACK (Acknowledgment) - 확인 응답 비트 │
│ ───────────────────────────────────────── │
│ • 확인 응답 번호 필드가 유효함을 표시 │
│ • 연결 설정 후 거의 항상 1로 설정 │
│ • "이전 데이터 잘 받았어" │
│ │
│ PSH (Push) - 푸시 비트 │
│ ───────────────────────────────────────── │
│ • 수신측에 즉시 전달 요청 │
│ • 버퍼에 쌓지 말고 바로 애플리케이션에 전달 │
│ • 대화형 통신에서 사용 (SSH, Telnet) │
│ │
│ RST (Reset) - 리셋 비트 │
│ ───────────────────────────────────────── │
│ • 연결 강제 종료 │
│ • 비정상적인 상황에서 연결 초기화 │
│ • 예: 존재하지 않는 포트에 연결 시도 │
│ │
│ SYN (Synchronize) - 동기화 비트 │
│ ───────────────────────────────────────── │
│ • 연결 설정 요청 │
│ • 순서 번호 동기화 │
│ • 3-Way Handshake의 첫 번째와 두 번째 단계에서 사용 │
│ │
│ FIN (Finish) - 종료 비트 │
│ ───────────────────────────────────────── │
│ • 연결 종료 요청 │
│ • "더 이상 보낼 데이터 없어" │
│ • 4-Way Handshake에서 사용 │
│ │
└─────────────────────────────────────────────────────────────┘플래그 조합 예시
주요 플래그 조합:
┌──────────┬─────┬─────┬─────┬─────┬─────┬─────┬──────────────┐
│ 상황 │ URG │ ACK │ PSH │ RST │ SYN │ FIN │ 설명 │
├──────────┼─────┼─────┼─────┼─────┼─────┼─────┼──────────────┤
│ 연결요청 │ 0 │ 0 │ 0 │ 0 │ 1 │ 0 │ SYN │
├──────────┼─────┼─────┼─────┼─────┼─────┼─────┼──────────────┤
│ 연결응답 │ 0 │ 1 │ 0 │ 0 │ 1 │ 0 │ SYN+ACK │
├──────────┼─────┼─────┼─────┼─────┼─────┼─────┼──────────────┤
│ 연결확인 │ 0 │ 1 │ 0 │ 0 │ 0 │ 0 │ ACK │
├──────────┼─────┼─────┼─────┼─────┼─────┼─────┼──────────────┤
│데이터전송│ 0 │ 1 │ 1 │ 0 │ 0 │ 0 │ ACK+PSH │
├──────────┼─────┼─────┼─────┼─────┼─────┼─────┼──────────────┤
│ 종료요청 │ 0 │ 1 │ 0 │ 0 │ 0 │ 1 │ FIN+ACK │
├──────────┼─────┼─────┼─────┼─────┼─────┼─────┼──────────────┤
│ 연결리셋 │ 0 │ 0 │ 0 │ 1 │ 0 │ 0 │ RST │
└──────────┴─────┴─────┴─────┴─────┴─────┴─────┴──────────────┘4. 3-Way Handshake (연결 설정)
연결 설정 과정
┌─────────────────────────────────────────────────────────────┐
│ 3-Way Handshake (TCP 연결 설정) │
├─────────────────────────────────────────────────────────────┤
│ │
│ [클라이언트] [서버] │
│ (CLOSED) (LISTEN) │
│ │ │ │
│ │ Step 1: SYN │ │
│ │ ──────────────────────────────────→ │ │
│ │ SYN=1, Seq=1000 │ │
│ │ "연결하고 싶어요!" │ │
│ │ │ │
│ (SYN_SENT) (SYN_RECEIVED) │
│ │ │ │
│ │ Step 2: SYN + ACK │ │
│ │ ←────────────────────────────────── │ │
│ │ SYN=1, ACK=1 │ │
│ │ Seq=2000, Ack=1001 │ │
│ │ "좋아요! 나도 연결해요!" │ │
│ │ │ │
│ │ Step 3: ACK │ │
│ │ ──────────────────────────────────→ │ │
│ │ ACK=1 │ │
│ │ Seq=1001, Ack=2001 │ │
│ │ "확인! 시작해요!" │ │
│ │ │ │
│ (ESTABLISHED) (ESTABLISHED) │
│ │ │ │
│ │ ═══════ 데이터 전송 가능 ═══════ │ │
│ │
└─────────────────────────────────────────────────────────────┘단계별 상세 설명
Step 1: SYN (연결 요청)
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
클라이언트 → 서버:
┌───────────────────────────────────────────────────────────┐
│ TCP 헤더 내용: │
│ • SYN = 1 (연결 요청) │
│ • Sequence Number = 1000 (초기 순서 번호, ISN) │
│ • ACK = 0 │
│ • Acknowledgment Number = 0 (아직 사용 안 함) │
│ │
│ 의미: │
│ "안녕하세요! 연결하고 싶어요. │
│ 내 순서 번호는 1000부터 시작할게요." │
└───────────────────────────────────────────────────────────┘
Step 2: SYN + ACK (연결 수락)
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
서버 → 클라이언트:
┌───────────────────────────────────────────────────────────┐
│ TCP 헤더 내용: │
│ • SYN = 1 (서버도 연결 요청) │
│ • ACK = 1 (클라이언트 SYN 확인) │
│ • Sequence Number = 2000 (서버의 ISN) │
│ • Acknowledgment Number = 1001 (다음에 받을 번호) │
│ │
│ 의미: │
│ "네! 연결 수락해요. │
│ 당신 SYN 잘 받았어요 (1000번). 다음은 1001번 보내세요. │
│ 내 순서 번호는 2000부터 시작할게요." │
└───────────────────────────────────────────────────────────┘
Step 3: ACK (연결 확인)
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
클라이언트 → 서버:
┌───────────────────────────────────────────────────────────┐
│ TCP 헤더 내용: │
│ • SYN = 0 │
│ • ACK = 1 (서버 SYN 확인) │
│ • Sequence Number = 1001 │
│ • Acknowledgment Number = 2001 (다음에 받을 번호) │
│ │
│ 의미: │
│ "알았어요! 서버 SYN 잘 받았어요 (2000번). │
│ 다음은 2001번 보내세요. 이제 시작해요!" │
└───────────────────────────────────────────────────────────┘
→ 연결 완료! 양쪽 모두 ESTABLISHED 상태왜 3-Way인가?
┌─────────────────────────────────────────────────────────────┐
│ 3-Way Handshake가 필요한 이유 │
├─────────────────────────────────────────────────────────────┤
│ │
│ 1. 양방향 통신 확인 │
│ ───────────────────────────────────────── │
│ • 클라이언트 → 서버: SYN으로 확인 │
│ • 서버 → 클라이언트: SYN+ACK로 확인 │
│ • 클라이언트 → 서버: ACK로 최종 확인 │
│ │
│ 2. 순서 번호 동기화 │
│ ───────────────────────────────────────── │
│ • 양쪽이 서로의 ISN을 교환 │
│ • 이후 데이터 순서 보장 가능 │
│ │
│ 3. 구형 패킷 방지 │
│ ───────────────────────────────────────── │
│ • 네트워크에 떠돌던 오래된 SYN 패킷 거부 │
│ • ACK 번호로 현재 연결 구분 │
│ │
│ │
│ 2-Way면 안 되는 이유: │
│ ───────────────────────────────────────── │
│ │
│ [클라이언트] [서버] │
│ │ │ │
│ │── SYN ──────────────────────→│ │
│ │ │ 연결 수립? │
│ │←─────────────────── SYN+ACK ─│ │
│ │ │ │
│ │ 서버는 클라이언트가 SYN+ACK를 │
│ │ 받았는지 확인할 수 없음! │
│ │ │ │
│ ╳ 클라이언트가 이미 사라졌을 수도 있음 │
│ │
└─────────────────────────────────────────────────────────────┘5. 4-Way Handshake (연결 종료)
연결 종료 과정
┌─────────────────────────────────────────────────────────────┐
│ 4-Way Handshake (TCP 연결 종료) │
├─────────────────────────────────────────────────────────────┤
│ │
│ [클라이언트] [서버] │
│ (ESTABLISHED) (ESTABLISHED) │
│ │ │ │
│ │ Step 1: FIN │ │
│ │ ──────────────────────────────────→ │ │
│ │ FIN=1, ACK=1 │ │
│ │ Seq=5000, Ack=3000 │ │
│ │ "나는 보낼 데이터 끝!" │ │
│ │ │ │
│ (FIN_WAIT_1) (CLOSE_WAIT) │
│ │ │ │
│ │ Step 2: ACK │ │
│ │ ←────────────────────────────────── │ │
│ │ ACK=1 │ │
│ │ Seq=3000, Ack=5001 │ │
│ │ "알겠어, 잠깐만 기다려" │ │
│ │ │ │
│ (FIN_WAIT_2) │ │
│ │ │ │
│ │ (서버가 남은 데이터 전송) │ │
│ │ │ │
│ │ Step 3: FIN (LAST_ACK) │
│ │ ←────────────────────────────────── │ │
│ │ FIN=1, ACK=1 │ │
│ │ Seq=3500, Ack=5001 │ │
│ │ "나도 보낼 데이터 끝!" │ │
│ │ │ │
│ (TIME_WAIT) │ │
│ │ │ │
│ │ Step 4: ACK │ │
│ │ ──────────────────────────────────→ │ │
│ │ ACK=1 │ │
│ │ Seq=5001, Ack=3501 │ │
│ │ "알겠어, 안녕!" │ │
│ │ │ │
│ │ (2MSL 대기: 약 60~240초) (CLOSED) │
│ │ │ │
│ (CLOSED) │ │
│ │
└─────────────────────────────────────────────────────────────┘왜 4-Way인가?
┌─────────────────────────────────────────────────────────────┐
│ 4-Way Handshake가 필요한 이유 │
├─────────────────────────────────────────────────────────────┤
│ │
│ 연결은 양방향이므로 각 방향을 따로 닫아야 함 │
│ │
│ 3-Way Handshake: │
│ • SYN과 ACK를 한 패킷에 담을 수 있음 (SYN+ACK) │
│ • 동시에 양방향 연결 시작 │
│ │
│ 4-Way Handshake: │
│ • FIN과 ACK를 따로 보내야 할 수 있음 │
│ • 한쪽이 종료해도 상대방은 보낼 데이터가 남아있을 수 있음 │
│ │
│ │
│ 예시: 파일 다운로드 │
│ ───────────────────────────────────────── │
│ │
│ [클라이언트] [서버] │
│ │ │ │
│ │── GET /file.zip ─────────→│ │
│ │ │ │
│ │←────── 파일 데이터 ────────│ │
│ │ (전송 중...) │ │
│ │ │ │
│ │── FIN ───────────────────→│ ← 클라이언트: "끝!" │
│ │ │ │
│ │←─────── ACK ───────────────│ ← 서버: "알았어" │
│ │ │ │
│ │←────── 남은 데이터 ─────────│ ← 아직 보낼 게 있음! │
│ │ │ │
│ │←─────── FIN ───────────────│ ← 서버: "이제 끝!" │
│ │ │ │
│ │── ACK ───────────────────→│ ← 클라이언트: "확인!" │
│ │
└─────────────────────────────────────────────────────────────┘Half-Close (반종료)
┌─────────────────────────────────────────────────────────────┐
│ Half-Close 상태 │
├─────────────────────────────────────────────────────────────┤
│ │
│ 클라이언트가 FIN을 보내고 서버가 ACK만 보낸 상태 │
│ │
│ ┌─────────────┐ ┌─────────────┐ │
│ │ 클라이언트 │ │ 서버 │ │
│ │ (FIN_WAIT_2)│ │(CLOSE_WAIT) │ │
│ ├─────────────┤ ├─────────────┤ │
│ │ │ │ │ │
│ │ 전송: X │ │ 전송: O │ │
│ │ 수신: O │ ←─────── │ 수신: X │ │
│ │ │ 데이터 │ │ │
│ │ │ │ │ │
│ └─────────────┘ └─────────────┘ │
│ │
│ • 클라이언트: 더 이상 데이터를 보내지 않음 (전송 종료) │
│ • 서버: 아직 데이터를 보낼 수 있음 (수신 가능) │
│ • 서버가 모든 데이터를 보낸 후 FIN 전송 │
│ │
└─────────────────────────────────────────────────────────────┘6. TCP 상태 다이어그램
전체 상태 전이도
┌─────────────────────────────────────────────────────────────┐
│ TCP 상태 전이도 │
├─────────────────────────────────────────────────────────────┤
│ │
│ ┌───────────┐ │
│ │ CLOSED │ │
│ └─────┬─────┘ │
│ │ │
│ ┌────────────────┴────────────────┐ │
│ │ │ │
│ ↓ (passive open) ↓ (active open) │
│ ┌─────────┐ ┌───────────┐ │
│ │ LISTEN │ │ SYN_SENT │ │
│ └────┬────┘ └─────┬─────┘ │
│ │ │ │
│ │ (recv SYN, │ (recv SYN+ACK,│
│ │ send SYN+ACK) │ send ACK) │
│ ↓ │ │
│ ┌──────────────┐ │ │
│ │ SYN_RECEIVED │ │ │
│ └──────┬───────┘ │ │
│ │ │ │
│ │ (recv ACK) │ │
│ ↓ ↓ │
│ ┌─────────────────────────────────────────┐ │
│ │ ESTABLISHED │ │
│ └──────────────────┬──────────────────────┘ │
│ │ │
│ ┌─────────────┴─────────────┐ │
│ │ (send FIN) │ (recv FIN, │
│ ↓ │ send ACK) │
│ ┌───────────┐ ↓ │
│ │ FIN_WAIT_1│ ┌───────────┐ │
│ └─────┬─────┘ │CLOSE_WAIT │ │
│ │ └─────┬─────┘ │
│ │ (recv ACK) │ (send FIN) │
│ ↓ ↓ │
│ ┌───────────┐ ┌───────────┐ │
│ │ FIN_WAIT_2│ │ LAST_ACK │ │
│ └─────┬─────┘ └─────┬─────┘ │
│ │ (recv FIN, │ (recv ACK) │
│ │ send ACK) │ │
│ ↓ ↓ │
│ ┌───────────┐ ┌───────────┐ │
│ │ TIME_WAIT │ │ CLOSED │ │
│ └─────┬─────┘ └───────────┘ │
│ │ (2MSL timeout) │
│ ↓ │
│ ┌───────────┐ │
│ │ CLOSED │ │
│ └───────────┘ │
│ │
└─────────────────────────────────────────────────────────────┘주요 상태 설명
┌─────────────────────────────────────────────────────────────┐
│ TCP 상태 상세 설명 │
├──────────────┬──────────────────────────────────────────────┤
│ 상태 │ 설명 │
├──────────────┼──────────────────────────────────────────────┤
│ CLOSED │ 연결 없음 (초기 상태) │
├──────────────┼──────────────────────────────────────────────┤
│ LISTEN │ 서버가 연결 요청 대기 중 │
├──────────────┼──────────────────────────────────────────────┤
│ SYN_SENT │ 클라이언트가 SYN 보내고 SYN+ACK 대기 │
├──────────────┼──────────────────────────────────────────────┤
│ SYN_RECEIVED │ 서버가 SYN 받고 SYN+ACK 보낸 후 ACK 대기 │
├──────────────┼──────────────────────────────────────────────┤
│ ESTABLISHED │ 연결 완료, 데이터 전송 가능 │
├──────────────┼──────────────────────────────────────────────┤
│ FIN_WAIT_1 │ FIN 보내고 ACK 대기 │
├──────────────┼──────────────────────────────────────────────┤
│ FIN_WAIT_2 │ FIN에 대한 ACK 받고, 상대방 FIN 대기 │
├──────────────┼──────────────────────────────────────────────┤
│ CLOSE_WAIT │ FIN 받고 ACK 보냄, 애플리케이션 종료 대기 │
├──────────────┼──────────────────────────────────────────────┤
│ LAST_ACK │ FIN 보내고 마지막 ACK 대기 │
├──────────────┼──────────────────────────────────────────────┤
│ TIME_WAIT │ 마지막 ACK 보낸 후 2MSL 동안 대기 │
├──────────────┼──────────────────────────────────────────────┤
│ CLOSING │ 동시 종료 시 발생 (드물게 발생) │
└──────────────┴──────────────────────────────────────────────┘TIME_WAIT 상태의 중요성
┌─────────────────────────────────────────────────────────────┐
│ TIME_WAIT 상태 │
├─────────────────────────────────────────────────────────────┤
│ │
│ 정의: │
│ • 마지막 ACK를 보낸 후 일정 시간 대기하는 상태 │
│ • 대기 시간: 2 × MSL (Maximum Segment Lifetime) │
│ • MSL: 패킷이 네트워크에서 생존할 수 있는 최대 시간 │
│ • 일반적으로 60초 ~ 240초 │
│ │
│ │
│ 필요한 이유: │
│ ───────────────────────────────────────── │
│ │
│ 1. 지연된 패킷 처리 │
│ • 네트워크에 떠돌던 이전 연결의 패킷이 │
│ 새 연결에 영향 주는 것 방지 │
│ │
│ 2. 마지막 ACK 재전송 │
│ • 상대방이 ACK를 못 받고 FIN을 재전송할 경우 │
│ 다시 ACK를 보낼 수 있음 │
│ │
│ │
│ 예시: │
│ ───────────────────────────────────────── │
│ │
│ [클라이언트] [서버] │
│ │ │ │
│ │←─────── FIN ─────────────────│ │
│ │ │ │
│ │── ACK ───────────────────────→│ │
│ │ (ACK 손실!) │
│ │ │ │
│ (TIME_WAIT) │ │
│ │ │ │
│ │←─────── FIN (재전송) ─────────│ │
│ │ │ │
│ │── ACK (재전송) ──────────────→│ │
│ │ │ │
│ 2MSL 후 (CLOSED) │
│ (CLOSED) │
│ │
│ │
│ 서버 포트 재사용 문제: │
│ ───────────────────────────────────────── │
│ • TIME_WAIT 동안 해당 포트 재사용 불가 │
│ • 서버 재시작 시 "Address already in use" 오류 발생 │
│ • 해결: SO_REUSEADDR 소켓 옵션 사용 │
│ │
└─────────────────────────────────────────────────────────────┘7. 순서 번호와 확인 응답
Sequence Number와 ACK Number 동작
┌─────────────────────────────────────────────────────────────┐
│ 순서 번호와 확인 응답 번호 │
├─────────────────────────────────────────────────────────────┤
│ │
│ [클라이언트] [서버] │
│ │
│ ───── 3-Way Handshake 완료 ───── │
│ 클라이언트 Seq: 1000 시작 │
│ 서버 Seq: 2000 시작 │
│ │
│ │ │ │
│ │ 데이터 100바이트 │ │
│ │ Seq=1001, Len=100 │ │
│ ├───────────────────────────────────→│ │
│ │ "1001~1100번 바이트 보내요" │ │
│ │ │ │
│ │ │ │
│ │ ACK=1101 │ │
│ │←───────────────────────────────────┤ │
│ │ "1101번부터 보내주세요" │ │
│ │ │ │
│ │ │ │
│ │ 데이터 200바이트 │ │
│ │ Seq=1101, Len=200 │ │
│ ├───────────────────────────────────→│ │
│ │ "1101~1300번 바이트 보내요" │ │
│ │ │ │
│ │ │ │
│ │ ACK=1301 │ │
│ │←───────────────────────────────────┤ │
│ │ "1301번부터 보내주세요" │ │
│ │ │ │
│ │
└─────────────────────────────────────────────────────────────┘
계산 공식:
┌─────────────────────────────────────────────────────────────┐
│ │
│ 다음 Sequence Number = 현재 Seq + 데이터 길이 │
│ │
│ ACK Number = 받은 Seq + 받은 데이터 길이 │
│ = 다음에 기대하는 Sequence Number │
│ │
│ │
│ 예시: │
│ • Seq=1001, 데이터=100바이트 전송 │
│ • 수신측 ACK = 1001 + 100 = 1101 │
│ • 의미: "1101번부터 보내줘!" │
│ │
└─────────────────────────────────────────────────────────────┘패킷 손실과 재전송
┌─────────────────────────────────────────────────────────────┐
│ 패킷 손실과 재전송 │
├─────────────────────────────────────────────────────────────┤
│ │
│ [송신측] [수신측] │
│ │ │ │
│ │ Seq=1001, Len=100 │ │
│ ├───────────────────────────────────────→│ │
│ │ │ ACK=1101 │
│ │←───────────────────────────────────────│ │
│ │ │ │
│ │ Seq=1101, Len=100 │ │
│ ├──────────────────╳ │ 손실! │
│ │ │ │
│ │ Seq=1201, Len=100 │ │
│ ├───────────────────────────────────────→│ │
│ │ │ │
│ │ ACK=1101 (중복!) │ │
│ │←───────────────────────────────────────│ │
│ │ "1101번 아직 안 받았어!" │ │
│ │ │ │
│ │ Seq=1301, Len=100 │ │
│ ├───────────────────────────────────────→│ │
│ │ │ │
│ │ ACK=1101 (중복!) │ │
│ │←───────────────────────────────────────│ │
│ │ │ │
│ │ Seq=1401, Len=100 │ │
│ ├───────────────────────────────────────→│ │
│ │ │ │
│ │ ACK=1101 (중복!) │ │
│ │←───────────────────────────────────────│ │
│ │ │ │
│ │ ┌────────────────────────────────┐ │ │
│ │ │ 중복 ACK 3개 → 빠른 재전송! │ │ │
│ │ └────────────────────────────────┘ │ │
│ │ │ │
│ │ Seq=1101, Len=100 (재전송) │ │
│ ├───────────────────────────────────────→│ │
│ │ │ │
│ │ ACK=1501 │ │
│ │←───────────────────────────────────────│ │
│ │ "1501번부터 보내줘!" │ │
│ │ (버퍼에 쌓인 패킷도 모두 확인됨) │ │
│ │
└─────────────────────────────────────────────────────────────┘
재전송 방식:
┌─────────────────────────────────────────────────────────────┐
│ │
│ 1. 타임아웃 재전송 (Retransmission Timeout, RTO) │
│ ───────────────────────────────────────── │
│ • ACK를 일정 시간 내에 못 받으면 재전송 │
│ • RTT(Round Trip Time) 기반으로 RTO 계산 │
│ • 느린 방식이지만 확실함 │
│ │
│ 2. 빠른 재전송 (Fast Retransmit) │
│ ───────────────────────────────────────── │
│ • 중복 ACK 3개 받으면 즉시 재전송 │
│ • 타임아웃을 기다리지 않음 │
│ • 빠른 복구 가능 │
│ │
└─────────────────────────────────────────────────────────────┘8. 흐름 제어와 윈도우 크기
슬라이딩 윈도우
┌─────────────────────────────────────────────────────────────┐
│ 슬라이딩 윈도우 │
├─────────────────────────────────────────────────────────────┤
│ │
│ 목적: │
│ • 송신측과 수신측의 처리 속도 차이 조절 │
│ • 수신 버퍼 오버플로우 방지 │
│ • 네트워크 효율성 향상 │
│ │
│ │
│ 동작 원리: │
│ ───────────────────────────────────────── │
│ │
│ 송신측 버퍼: │
│ ┌──┬──┬──┬──┬──┬──┬──┬──┬──┬──┬──┬──┐ │
│ │✓ │✓ │✓ │1 │2 │3 │4 │ 5│ 6│ 7│ 8│ 9│ │
│ └──┴──┴──┴──┴──┴──┴──┴──┴──┴──┴──┴──┘ │
│ ACK됨 │← 전송됨, ACK 대기 →│← 전송 가능 →│ │
│ │ │ │ │
│ └─────── 윈도우 크기: 6 ────────────┘ │
│ │
│ │
│ 수신측이 윈도우 크기 조절: │
│ • 버퍼 여유 많음 → 윈도우 크기 증가 → 빠른 전송 │
│ • 버퍼 부족 → 윈도우 크기 감소 → 전송 속도 감소 │
│ • 버퍼 가득 참 → 윈도우 크기 0 → 전송 중단 │
│ │
└─────────────────────────────────────────────────────────────┘윈도우 크기 조절 예시
┌─────────────────────────────────────────────────────────────┐
│ 윈도우 크기 조절 과정 │
├─────────────────────────────────────────────────────────────┤
│ │
│ [송신측] [수신측] │
│ │ │ │
│ │ 3-Way Handshake │ │
│ │ (Window=4096 광고) │ │
│ │←───────────────────────────────────────│ │
│ │ │ │
│ │ 1000바이트 전송 │ │
│ ├───────────────────────────────────────→│ │
│ │ │ 버퍼: 1000 │
│ │ │ │
│ │ 1000바이트 전송 │ │
│ ├───────────────────────────────────────→│ │
│ │ │ 버퍼: 2000 │
│ │ │ │
│ │ ACK, Window=2096 │ │
│ │←───────────────────────────────────────│ 처리 중... │
│ │ "2096바이트만 더 받을 수 있어" │ │
│ │ │ │
│ │ 1000바이트 전송 │ │
│ ├───────────────────────────────────────→│ │
│ │ │ 버퍼: 2000 │
│ │ │ │
│ │ 1000바이트 전송 │ │
│ ├───────────────────────────────────────→│ │
│ │ │ 버퍼: 3000 │
│ │ │ │
│ │ ACK, Window=0 │ │
│ │←───────────────────────────────────────│ 버퍼 가득! │
│ │ "잠깐! 더 보내지 마!" │ │
│ │ │ │
│ │ ═══════ 전송 중단 ═══════ │ │
│ │ │ │
│ │ (수신측이 데이터 처리 중) │ │
│ │ │ │
│ │ Window Update=2000 │ │
│ │←───────────────────────────────────────│ 버퍼 비움 │
│ │ "2000바이트 받을 수 있어!" │ │
│ │ │ │
│ │ ═══════ 전송 재개 ═══════ │ │
│ │
└─────────────────────────────────────────────────────────────┘Zero Window와 Probe
┌─────────────────────────────────────────────────────────────┐
│ Zero Window Probe │
├─────────────────────────────────────────────────────────────┤
│ │
│ 문제 상황: │
│ • 수신측이 Window=0을 보냄 │
│ • 송신측은 전송 중단 │
│ • 수신측이 보낸 Window Update가 손실되면? │
│ → 양쪽 모두 영원히 대기 (Deadlock!) │
│ │
│ │
│ 해결: Zero Window Probe │
│ ───────────────────────────────────────── │
│ │
│ [송신측] [수신측] │
│ │ │ │
│ │ ACK, Window=0 │ │
│ │←───────────────────────────────────────│ │
│ │ │ │
│ │ (일정 시간 후) │ │
│ │ │ │
│ │ Zero Window Probe (1바이트) │ │
│ ├───────────────────────────────────────→│ │
│ │ "아직 버퍼 비었어?" │ │
│ │ │ │
│ │ ACK, Window=1000 │ │
│ │←───────────────────────────────────────│ │
│ │ "응! 1000바이트 받을 수 있어" │ │
│ │ │ │
│ │ ═══════ 전송 재개 ═══════ │ │
│ │
└─────────────────────────────────────────────────────────────┘9. 실무 활용
TCP 연결 상태 확인
Windows에서 TCP 연결 확인:
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
> netstat -an
활성 연결
프로토콜 로컬 주소 외부 주소 상태
TCP 0.0.0.0:80 0.0.0.0:0 LISTENING
TCP 0.0.0.0:443 0.0.0.0:0 LISTENING
TCP 127.0.0.1:3306 0.0.0.0:0 LISTENING
TCP 192.168.1.10:52341 142.250.185.78:443 ESTABLISHED
TCP 192.168.1.10:52342 20.189.173.1:443 TIME_WAIT
TCP 192.168.1.10:52343 52.109.88.45:443 CLOSE_WAIT
상태별 의미:
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
• LISTENING : 서버가 연결 대기 중
• ESTABLISHED : 정상 연결 상태
• TIME_WAIT : 연결 종료 후 대기 중
• CLOSE_WAIT : 상대방이 연결 종료 요청함 (애플리케이션 종료 필요)
• FIN_WAIT_1/2 : 연결 종료 진행 중
• SYN_SENT : 연결 요청 보냄
• SYN_RECEIVED : 연결 요청 받음
Linux에서 TCP 연결 확인:
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
$ ss -tan
State Recv-Q Send-Q Local Address:Port Peer Address:Port
LISTEN 0 128 0.0.0.0:80 0.0.0.0:*
LISTEN 0 128 0.0.0.0:443 0.0.0.0:*
ESTAB 0 0 192.168.1.10:52341 142.250.185.78:443
TIME-WAIT 0 0 192.168.1.10:52342 20.189.173.1:443
$ netstat -tnp
Proto Recv-Q Send-Q Local Address Foreign Address State PID/Program
tcp 0 0 0.0.0.0:80 0.0.0.0:* LISTEN 1234/nginx
tcp 0 0 192.168.1.10:52341 142.250.185.78:443 ESTABLISHED 5678/chromeWireshark로 TCP 분석
Wireshark에서 3-Way Handshake 분석:
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
No. Time Source Destination Protocol Info
1 0.0000 192.168.1.10 142.250.185.78 TCP 52341→443 [SYN] Seq=0
2 0.0150 142.250.185.78 192.168.1.10 TCP 443→52341 [SYN, ACK] Seq=0 Ack=1
3 0.0151 192.168.1.10 142.250.185.78 TCP 52341→443 [ACK] Seq=1 Ack=1
패킷 1번 상세:
┌───────────────────────────────────────────────────────────┐
│ Transmission Control Protocol │
│ ├─ Source Port: 52341 │
│ ├─ Destination Port: 443 │
│ ├─ Sequence Number: 0 (relative) │
│ ├─ Acknowledgment Number: 0 │
│ ├─ Header Length: 40 bytes │
│ ├─ Flags: 0x002 (SYN) │
│ │ ├─ SYN: Set │
│ │ └─ 나머지: Not set │
│ ├─ Window: 65535 │
│ ├─ Checksum: 0x1234 │
│ └─ Options: │
│ ├─ MSS: 1460 │
│ ├─ SACK Permitted │
│ └─ Window Scale: 8 │
└───────────────────────────────────────────────────────────┘
Wireshark 필터:
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
• tcp.flags.syn == 1 # SYN 패킷만
• tcp.flags.syn == 1 && tcp.flags.ack == 1 # SYN+ACK만
• tcp.flags.fin == 1 # FIN 패킷만
• tcp.flags.reset == 1 # RST 패킷만
• tcp.analysis.retransmission # 재전송 패킷
• tcp.analysis.duplicate_ack # 중복 ACK
• tcp.port == 443 # 443 포트 통신
• ip.addr == 192.168.1.10 && tcp # 특정 IP의 TCP 통신일반적인 TCP 문제와 해결
┌─────────────────────────────────────────────────────────────┐
│ TCP 문제 해결 가이드 │
├─────────────────────────────────────────────────────────────┤
│ │
│ 1. CLOSE_WAIT 상태가 계속 쌓이는 경우 │
│ ───────────────────────────────────────── │
│ 원인: 애플리케이션이 소켓을 제대로 close()하지 않음 │
│ 해결: 애플리케이션 코드에서 소켓 닫기 확인 │
│ │
│ 확인: │
│ > netstat -an | find "CLOSE_WAIT" │
│ │
│ │
│ 2. TIME_WAIT 상태가 너무 많은 경우 │
│ ───────────────────────────────────────── │
│ 원인: 짧은 연결이 많이 발생 (예: HTTP 요청) │
│ 해결: │
│ • 연결 재사용 (Keep-Alive) │
│ • SO_REUSEADDR 옵션 사용 │
│ • 커널 파라미터 조정 (Linux) │
│ │
│ Linux 커널 파라미터: │
│ $ sysctl -w net.ipv4.tcp_tw_reuse=1 │
│ │
│ │
│ 3. 연결이 자주 끊기는 경우 │
│ ───────────────────────────────────────── │
│ 원인: │
│ • 방화벽 타임아웃 │
│ • 네트워크 불안정 │
│ • Keep-Alive 설정 없음 │
│ │
│ 해결: │
│ • TCP Keep-Alive 활성화 │
│ • 애플리케이션 레벨 하트비트 │
│ │
│ │
│ 4. 연결 지연 (느린 연결) │
│ ───────────────────────────────────────── │
│ 원인: │
│ • DNS 지연 │
│ • 네트워크 지연 (높은 RTT) │
│ • 패킷 손실로 인한 재전송 │
│ │
│ 확인: │
│ > ping [서버주소] │
│ > tracert [서버주소] │
│ │
│ │
│ 5. RST로 연결이 끊기는 경우 │
│ ───────────────────────────────────────── │
│ 원인: │
│ • 서버 포트가 닫혀있음 │
│ • 방화벽에서 차단 │
│ • 서버 애플리케이션 비정상 종료 │
│ │
│ 확인: │
│ > telnet [서버주소] [포트] │
│ │
└─────────────────────────────────────────────────────────────┘핵심 정리
| 개념 | 설명 |
|---|---|
| TCP 세그먼트 | TCP 헤더가 붙은 데이터 단위 |
| 순서 번호 | 전송하는 데이터의 바이트 순서 |
| 확인 응답 번호 | 다음에 받기 기대하는 바이트 번호 |
| SYN | 연결 시작 요청 플래그 |
| ACK | 확인 응답 플래그 |
| FIN | 연결 종료 요청 플래그 |
| RST | 연결 강제 리셋 플래그 |
| 3-Way Handshake | TCP 연결 설정 (SYN → SYN+ACK → ACK) |
| 4-Way Handshake | TCP 연결 종료 (FIN → ACK → FIN → ACK) |
| TIME_WAIT | 연결 종료 후 2MSL 동안 대기하는 상태 |
| 윈도우 크기 | 수신 가능한 데이터 크기 (흐름 제어) |
TCP 연결 과정 요약
┌─────────────────────────────────────────────────────────────┐
│ TCP 연결 흐름 │
├─────────────────────────────────────────────────────────────┤
│ │
│ 1. 연결 설정 (3-Way Handshake) │
│ ───────────────────────────────────────── │
│ 클라이언트 서버 │
│ │ SYN (연결할래요!) │ │
│ ├─────────────────────→│ │
│ │ SYN+ACK (좋아요!) │ │
│ │←─────────────────────┤ │
│ │ ACK (시작!) │ │
│ ├─────────────────────→│ │
│ │
│ 2. 데이터 전송 │
│ ───────────────────────────────────────── │
│ • Seq/Ack로 순서 보장 │
│ • 윈도우로 흐름 제어 │
│ • 손실 시 재전송 │
│ │
│ 3. 연결 종료 (4-Way Handshake) │
│ ───────────────────────────────────────── │
│ 클라이언트 서버 │
│ │ FIN (끝날래요!) │ │
│ ├─────────────────────→│ │
│ │ ACK (잠깐만요) │ │
│ │←─────────────────────┤ │
│ │ FIN (나도 끝!) │ │
│ │←─────────────────────┤ │
│ │ ACK (안녕!) │ │
│ ├─────────────────────→│ │
│ │ │ │
│ (TIME_WAIT) (CLOSED) │
│ │
└─────────────────────────────────────────────────────────────┘주요 명령어 정리
TCP 연결 상태 확인:
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
Windows:
> netstat -an # 모든 연결 상태
> netstat -ano # PID 포함
> netstat -an | find "ESTABLISHED" # 연결된 것만
Linux:
$ ss -tan # TCP 연결 상태
$ ss -tanp # 프로세스 정보 포함
$ netstat -tnp # TCP 연결과 프로세스
포트 확인:
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
Windows:
> netstat -ano | find "LISTEN"
> netstat -ano | find ":80"
Linux:
$ ss -tlnp # LISTEN 중인 TCP 포트
$ lsof -i :80 # 80포트 사용 프로세스
연결 테스트:
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
$ telnet [host] [port] # TCP 연결 테스트
$ nc -zv [host] [port] # 포트 스캔
$ curl -v [url] # HTTP 연결 상세용어 정리
- TCP (Transmission Control Protocol): 연결 지향형, 신뢰성 있는 전송 계층 프로토콜
- 세그먼트 (Segment): TCP 헤더가 붙은 데이터 단위
- 순서 번호 (Sequence Number): 전송하는 데이터의 바이트 순서 번호
- 확인 응답 번호 (Acknowledgment Number): 다음에 받기 기대하는 바이트 번호
- 제어 비트 (Control Flags): SYN, ACK, FIN, RST, PSH, URG의 6개 플래그
- 3-Way Handshake: TCP 연결 설정 과정 (SYN → SYN+ACK → ACK)
- 4-Way Handshake: TCP 연결 종료 과정 (FIN → ACK → FIN → ACK)
- TIME_WAIT: 연결 종료 후 2MSL 동안 대기하는 상태
- MSL (Maximum Segment Lifetime): 패킷이 네트워크에서 생존할 수 있는 최대 시간
- 윈도우 크기 (Window Size): 수신측이 한 번에 받을 수 있는 데이터 크기
- 슬라이딩 윈도우 (Sliding Window): 흐름 제어를 위한 윈도우 관리 기법
- ISN (Initial Sequence Number): 연결 시 사용하는 초기 순서 번호