TCP 연결 동작 순서: 3-way handshake부터 ACK·재전송·종료까지

반응형

TCP는 애플리케이션에 신뢰할 수 있고 순서가 보장되는 양방향 바이트 스트림을 제공하는 연결 지향 전송 프로토콜이다. 이 문장 안에는 연결 설정, 순서 번호, 확인 응답, 재전송, 흐름 제어와 연결 종료가 모두 들어 있다.

기존 글에는 TCP reliable data transfer, ACK generation, fast retransmit, flow control, 3-way handshake, 연결 종료라는 공부 항목만 남아 있었다. 이번에는 한 연결이 열리고 데이터를 주고받은 뒤 닫히는 순서로 묶었다.

TCP에서 연결이 있다는 뜻

TCP 연결의 양 끝은 다음 정보와 상태를 관리한다.

  • 출발지·목적지 IP 주소와 포트
  • 송신·수신 순서 번호 공간
  • 아직 확인받지 못한 바이트 범위
  • 상대가 받을 수 있다고 알린 수신 윈도
  • 재전송 타이머와 혼잡 제어 상태
  • SYN-SENT, ESTABLISHED, FIN-WAIT, TIME-WAIT 같은 연결 상태

IP가 패킷 하나를 전달하는 것과 달리 TCP는 앞뒤 세그먼트의 관계를 기억한다. 그 상태를 양쪽에서 합의하는 첫 과정이 3-way handshake다.

3-way handshake는 양쪽 순서 번호를 동기화한다

연결 설정의 기본 흐름은 다음과 같다.

클라이언트                              서버
   |                                     |
   |  SYN, Seq=x                         |
   | ----------------------------------> |
   |                                     |
   |  SYN+ACK, Seq=y, Ack=x+1            |
   | <---------------------------------- |
   |                                     |
   |  ACK, Ack=y+1                       |
   | ----------------------------------> |
   |           ESTABLISHED               |
  1. 클라이언트가 자신의 초기 순서 번호 x와 함께 SYN을 보낸다.
  2. 서버가 클라이언트의 SYN을 확인하고, 자신의 초기 순서 번호 y를 담은 SYN+ACK를 보낸다.
  3. 클라이언트가 서버의 SYN을 확인하는 ACK를 보낸다.

SYN과 FIN은 각각 순서 번호 공간에서 한 칸을 소비한다. 그래서 SYN Seq=x에 대한 확인 번호가 x+1이 된다.

왜 2-way handshake로는 부족한가

양쪽 모두 자신의 초기 순서 번호를 알리고 상대가 이를 받았음을 확인해야 한다. 오래전에 지연된 중복 SYN이 도착하는 상황에서도 한쪽의 응답만 보고 새 연결이 성립했다고 오인하지 않아야 한다.

3단계는 클라이언트가 서버의 초기 순서 번호까지 확인했다는 사실을 서버가 알게 한다. 단순히 “서버가 살아 있는지 세 번 확인한다”는 설명보다 순서 번호 동기화 관점으로 보는 편이 정확하다.

데이터는 세그먼트가 아니라 바이트 번호로 확인한다

TCP는 메시지 경계를 보존하지 않는 바이트 스트림이다. 애플리케이션이 한 번 write한 데이터가 네트워크에서 한 세그먼트로 전달된다고 보장하지 않는다.

송신자는 전송하는 바이트에 순서 번호를 붙이고, 수신자는 다음에 받고 싶은 바이트 번호를 ACK로 알린다.

송신: Seq=1001, Len=500
수신: Ack=1501

Ack=1501은 1500번까지 연속된 바이트를 받았고 다음에는 1501번을 기대한다는 뜻이다. 기본 ACK는 누적 확인 응답이므로 앞선 여러 세그먼트를 한 번에 확인할 수 있다.

체크섬은 전송 중 오류를 감지하는 역할을 하고, 순서 번호와 ACK는 손실·중복·순서 뒤바뀜을 판단하는 기준이 된다. 오류나 손실을 감지한 뒤에는 필요한 데이터를 재전송한다.

송신 측에서는 네 가지 사건을 구분한다

애플리케이션이 데이터를 쓴다

TCP는 데이터를 송신 버퍼에 두고, 흐름 제어와 혼잡 제어가 허용하는 범위에서 세그먼트를 만든다. 애플리케이션의 write 성공은 상대 애플리케이션이 데이터를 처리했다는 뜻이 아니다.

새로운 ACK가 도착한다

확인된 바이트 범위를 송신 버퍼에서 정리하고, 왕복 시간 추정과 혼잡 제어 상태를 갱신할 수 있다. ACK 번호가 실제로 앞으로 진행했는지 확인해야 한다.

재전송 타이머가 만료된다

ACK가 제한 시간 안에 오지 않으면 가장 오래 확인받지 못한 데이터를 재전송한다. 재전송 타임아웃(RTO)은 고정 숫자가 아니라 측정한 왕복 시간과 변동을 바탕으로 계산한다. RFC 6298은 RTO 계산과 지수 백오프의 기준을 정의한다.

중복 ACK가 이어진다

뒤의 데이터는 도착했지만 중간 바이트가 비어 있으면 같은 ACK 번호가 반복될 수 있다. RFC 5681의 고전적인 fast retransmit은 세 번의 중복 ACK를 손실 신호로 보고 타이머 만료 전에 빠진 세그먼트를 재전송한다.

실제 운영체제는 SACK와 더 발전된 손실 복구 알고리즘을 함께 사용할 수 있다. 따라서 패킷 캡처 하나를 보고 모든 구현이 Reno의 단순한 규칙만 따른다고 단정하지 않는다.

흐름 제어와 혼잡 제어는 다른 제한이다

TCP 송신량에는 적어도 두 가지 창이 영향을 준다.

보호 대상 기준
수신 윈도 rwnd 수신 호스트의 버퍼 수신자가 TCP 헤더의 Window 필드로 광고
혼잡 윈도 cwnd 네트워크 경로 송신자가 ACK·손실·ECN 신호로 조절

실제로 전송 가능한 미확인 데이터는 두 제약 가운데 더 작은 쪽에 묶인다. 수신 애플리케이션이 데이터를 늦게 읽으면 rwnd가 줄거나 0이 될 수 있다. 네트워크가 혼잡하면 수신 버퍼가 여유 있어도 cwnd가 전송을 제한한다.

혼잡 윈도의 증가·감소와 slow start, fast recovery는 TCP 혼잡 제어 동작 순서에서 따로 정리했다.

연결 종료는 양방향을 따로 닫는다

TCP는 양방향 통신이므로 한쪽의 송신 종료가 곧 반대 방향 수신 종료를 뜻하지 않는다.

일반적으로 다음과 같은 흐름을 자주 본다.

클라이언트                              서버
   |  FIN ----------------------------> |
   | <----------------------------- ACK |
   |                                     |
   | <----------------------------- FIN  |
   |  ACK ----------------------------> |

첫 FIN은 “나는 더 보낼 데이터가 없다”는 뜻이다. 상대는 FIN을 확인한 뒤에도 자신의 남은 데이터를 보낼 수 있다. 이것이 half-close가 가능한 이유다.

종료가 항상 정확히 네 개의 독립된 패킷으로 보이는 것은 아니다. ACK와 FIN이 하나의 세그먼트에 함께 실릴 수도 있고, 양쪽이 동시에 닫을 수도 있다.

마지막 ACK를 보낸 쪽은 보통 TIME-WAIT 상태를 거친다. 이 시간은 마지막 ACK가 손실됐을 때 상대의 FIN 재전송에 다시 답하고, 같은 연결 식별자의 오래된 세그먼트가 새 연결과 섞이는 위험을 줄이는 데 필요하다. TIME-WAIT가 많다는 사실만으로 즉시 누수라고 결론 내리지 않는다.

패킷을 볼 때 상태와 번호를 함께 읽는다

Linux에서는 연결 상태를 다음처럼 볼 수 있다.

ss -tan

패킷 캡처 권한과 개인정보 범위를 확인한 뒤 특정 테스트 포트의 TCP 헤더를 관찰할 수 있다.

sudo tcpdump -nn -i any 'tcp port 443'

HTTPS의 애플리케이션 내용은 암호화되지만 TCP 플래그와 순서·확인 번호, 윈도 정보는 관찰할 수 있다. 운영 트래픽을 무단으로 수집하거나 페이로드를 저장하지 않는다.

분석할 때는 다음 질문을 순서대로 본다.

  1. SYN, SYN+ACK, ACK 중 어디에서 멈췄는가?
  2. ACK 번호가 앞으로 진행하는가?
  3. 재전송은 RTO 이후인가, 중복 ACK 이후인가?
  4. 수신 윈도와 혼잡 윈도 중 어느 쪽이 제한하는가?
  5. FIN, RST, timeout 중 어떤 방식으로 연결이 끝났는가?
  6. 패킷 현상과 애플리케이션 로그의 시간이 맞는가?

이 글은 TCP 표준과 기존 학습 항목을 연결한 개념 정리다. 특정 시스템에서 캡처를 수행하거나 장애를 해결한 결과를 추가한 것은 아니다.

참고 자료

반응형
KEEP READING
카테고리 전체 보기 →

댓글