TCP 연결은 어떻게 종료되는가: FIN·RST·TIME_WAIT 이해하기

TCP 연결은 어떻게 종료되는가: FIN·RST·TIME_WAIT 이해하기#

1장 연결이 만들어지는 것만큼 종료도 중요하다#

1.1 연결이 끝났는데 왜 소켓은 계속 남아 있을까#

주차관제 Server에 수십 대의 Controller가 TCP로 연결되어 있다고 가정해보겠습니다.

처음에는 정상적으로 동작합니다.

그런데 시간이 지나면서:

새로운 장비 연결 지연

접속 가능한 장비 수 감소

TCP Socket 수 증가

Server Memory 사용량 증가

같은 현상이 나타납니다.

ss 명령으로 확인했더니 다음과 같은 상태가 많이 보입니다.

TIME-WAIT

CLOSE-WAIT

FIN-WAIT-1

FIN-WAIT-2

이 상태들을 모르고 보면 모두:

연결이 제대로 안 끊긴 것 같다.

라고 생각하기 쉽습니다.

하지만 TCP는 연결을 만들 때뿐 아니라 연결을 종료할 때도 상태를 관리합니다.

어떤 상태는 정상이고, 어떤 상태가 지나치게 오래 쌓인다면 Application 문제의 강력한 단서가 될 수 있습니다.


1.2 TCP는 양방향 통신이다#

TCP Connection을 다음과 같이 생각해보겠습니다.

Client
  ⇄
Server

TCP는 Full Duplex 통신이므로:

Client → Server

Server → Client

두 방향이 독립적으로 존재합니다.

따라서 한쪽이:

나는 이제 더 이상 데이터를 보내지 않겠다.

고 선언해도 상대방은 아직 데이터를 보낼 수 있습니다.

이 점을 이해해야 FIN의 의미를 정확하게 이해할 수 있습니다.


2장 FIN은 무엇을 의미하는가#

2.1 FIN은 Connection 전체를 즉시 없애는 신호가 아니다#

FIN은 TCP Header의 Flag 중 하나입니다.

FIN을 보낸다는 것은 기본적으로:

이 방향에서는 더 이상 보낼 데이터가 없다.

는 의미입니다.

예를 들어 Client가 FIN을 보냈다고 가정합니다.

Client                         Server

  FIN
─────────────────────────────→

이것은:

Client → Server

더 이상 데이터 전송하지 않음

을 의미합니다.

하지만 반대 방향인:

Server → Client

까지 즉시 종료됐다는 뜻은 아닙니다.


2.2 TCP의 Half-Close#

TCP에서는 한 방향만 먼저 종료될 수 있습니다.

이를 이해하기 쉽게 Half-Close라고 부릅니다.

예:

Client → Server
종료

Server → Client
아직 가능

즉:

Client
FIN
──────→ Server

Client
←────── Server Data

같은 상황도 가능합니다.

이 구조 때문에 TCP 정상 종료가 단순히:

FIN
→ 끝

이 아니라 여러 단계로 진행됩니다.


3장 정상적인 TCP 종료 흐름#

3.1 첫 번째 FIN#

Client가 먼저 Connection 종료를 시작한다고 가정하겠습니다.

Client                         Server

FIN
─────────────────────────────→

Client 입장에서는:

더 이상 보낼 데이터가 없음

을 알립니다.


3.2 Server가 ACK한다#

Server는 FIN을 받으면 이를 확인합니다.

Client                         Server

FIN
─────────────────────────────→

                         ACK
←─────────────────────────────

이 순간 Server는 Client가 보내는 방향이 종료됐다는 것을 압니다.

하지만 Server가 아직 보낼 데이터가 있다면 계속 보낼 수 있습니다.


3.3 Server도 자신의 FIN을 보낸다#

Server Application까지 데이터 처리를 마치고 자신도 더 이상 보낼 데이터가 없다면 FIN을 보냅니다.

Client                         Server

FIN
─────────────────────────────→

                         ACK
←─────────────────────────────

                         FIN
←─────────────────────────────

3.4 마지막 ACK#

Client는 Server의 FIN을 확인합니다.

Client                         Server

FIN
─────────────────────────────→

                         ACK
←─────────────────────────────

                         FIN
←─────────────────────────────

ACK
─────────────────────────────→

이를 흔히 4-Way Handshake라고 설명합니다.

다만 실제 Packet에서는 ACK와 FIN이 하나의 Segment에 함께 포함되는 경우도 있으므로 항상 정확히 네 개의 Packet으로만 보이는 것은 아닙니다.


4장 FIN과 ACK의 Sequence Number 관계#

4.1 FIN도 Sequence Number 공간을 하나 사용한다#

앞서 TCP SYN이 Sequence Number 공간에서 하나를 사용한다고 배웠습니다.

FIN도 마찬가지입니다.

예를 들어 Client가:

FIN
Seq = 5000

을 보냈다면 Server의 ACK는:

Ack = 5001

이 될 수 있습니다.

즉:

FIN Seq 5000
        ↓
ACK 5001

입니다.

Packet Capture에서 정상 종료를 읽을 때 이 관계를 확인하면 도움이 됩니다.


5장 TCP 종료 상태를 이해하자#

TCP 종료 장애를 분석하려면 몇 가지 상태를 알아야 합니다.

대표적으로:

ESTABLISHED

FIN_WAIT_1

FIN_WAIT_2

CLOSE_WAIT

LAST_ACK

TIME_WAIT

CLOSED

가 있습니다.

이 상태들은 모두 같은 문제가 아니라 TCP 종료 과정의 서로 다른 위치를 나타냅니다.


6장 FIN_WAIT_1과 FIN_WAIT_2#

6.1 FIN_WAIT_1#

먼저 FIN을 보낸 쪽은 일반적으로:

ESTABLISHED
↓
FIN 전송
↓
FIN_WAIT_1

상태로 이동합니다.

이 상태에서는 자신이 보낸 FIN에 대한 ACK를 기다리고 있습니다.


6.2 FIN_WAIT_2#

상대방이 FIN을 ACK하면:

FIN_WAIT_1
↓
FIN에 대한 ACK 수신
↓
FIN_WAIT_2

가 될 수 있습니다.

이제 자신은 데이터를 모두 보냈지만 상대방은 아직 자신의 FIN을 보내지 않은 상태입니다.

개념적으로:

나
더 이상 송신 안 함

상대
아직 송신 가능

입니다.


7장 CLOSE_WAIT는 무엇인가#

7.1 상대방의 FIN을 받은 쪽에서 나타난다#

Server가 Client의 FIN을 받았다고 가정합니다.

Server TCP Stack은 FIN에 ACK를 보냅니다.

그리고 Server 쪽 Socket은:

ESTABLISHED
↓
상대 FIN 수신
↓
CLOSE_WAIT

상태가 될 수 있습니다.

이 이름을 그대로 해석하면:

상대는 연결을 닫겠다고 했고, 이제 내 Application이 이 Socket을 닫기를 기다리는 상태

라고 이해할 수 있습니다.


7.2 CLOSE_WAIT는 TCP가 기다리는 문제가 아니라 Application이 닫기를 기다리는 상태다#

매우 중요합니다.

CLOSE_WAIT에서 TCP Stack은 이미 상대방의 FIN을 받았습니다.

이제 Application이:

close()

같은 종료 처리를 해야 합니다.

Application이 Socket을 제대로 닫지 않으면 CLOSE_WAIT가 계속 남아 있을 수 있습니다.

따라서:

CLOSE_WAIT가 대량으로 오래 유지

된다면 가장 먼저:

Application이 Socket을 제대로 close하는가?

를 확인해야 합니다.


8장 LAST_ACK는 무엇인가#

CLOSE_WAIT 상태의 Application이 Socket을 닫으면 자신의 FIN을 보냅니다.

CLOSE_WAIT
↓
Application close()
↓
FIN 전송
↓
LAST_ACK

LAST_ACK 상태에서는:

내가 보낸 마지막 FIN에 대한 ACK를 기다리고 있다.

라고 이해할 수 있습니다.

상대방의 ACK를 받으면 Connection 상태가 최종적으로 정리됩니다.


9장 TIME_WAIT는 무엇인가#

9.1 마지막 ACK를 보낸 쪽에서 주로 나타난다#

정상적인 종료에서 마지막 FIN을 받고 ACK를 보낸 쪽은 즉시 Connection 정보를 모두 없애지 않습니다.

대신 일정 시간:

TIME_WAIT

상태를 유지합니다.


9.2 TIME_WAIT가 필요한 첫 번째 이유 — 마지막 ACK 유실#

다음 상황을 생각해보겠습니다.

Server
FIN
──────→ Client

Client
ACK
──────→ Server

그런데 마지막 ACK가 Network에서 사라졌습니다.

Server는 자신의 FIN이 확인되지 않았다고 생각하고 FIN을 다시 보낼 수 있습니다.

Server
FIN 재전송
──────→ Client

Client가 TIME_WAIT 상태를 유지하고 있다면 이 FIN을 인식하고 마지막 ACK를 다시 보낼 수 있습니다.


9.3 TIME_WAIT가 필요한 두 번째 이유 — 오래된 Segment 제거#

Network에서 지연된 Segment가 한참 뒤에 도착할 가능성도 고려해야 합니다.

예를 들어 기존 Connection이 끝난 직후 동일한:

Source IP
Source Port
Destination IP
Destination Port

조합을 새로운 Connection에서 바로 재사용한다고 생각해보겠습니다.

이전 Connection의 늦은 Segment가 새로운 Connection에 섞이면 문제가 될 수 있습니다.

TIME_WAIT은 이런 오래된 Segment가 Network에서 사라질 시간을 확보하는 역할도 합니다.


9.4 TIME_WAIT 자체는 오류가 아니다#

Server에서:

TIME-WAIT
TIME-WAIT
TIME-WAIT
TIME-WAIT

가 많이 보이면 처음에는 장애처럼 느껴질 수 있습니다.

하지만 짧은 TCP Connection을 많이 만드는 시스템에서는 TIME_WAIT가 많이 생기는 것이 자연스러울 수도 있습니다.

중요한 것은 단순 개수가 아니라:

Connection 생성률

동시 Connection 수

Ephemeral Port 사용량

File Descriptor

Memory

Application 구조

와 함께 보는 것입니다.


10장 TIME_WAIT와 Port 고갈#

10.1 Client가 매우 많은 Outbound Connection을 반복한다면#

예를 들어 Client가 Server에 연결할 때마다 새로운 Source Port를 사용한다고 하겠습니다.

192.168.10.20:50001
→ Server:443

192.168.10.20:50002
→ Server:443

192.168.10.20:50003
→ Server:443

짧은 Connection을 매우 빠르게 만들고 닫으면 많은 Source Port가 TIME_WAIT와 함께 일정 시간 묶일 수 있습니다.

극단적인 경우 사용 가능한 Ephemeral Port 범위에 압박을 줄 수 있습니다.


10.2 Server Port 443 자체가 TIME_WAIT 때문에 사라지는 것은 아니다#

여기서 자주 생기는 오해가 있습니다.

Server가:

TCP 443
LISTEN

하고 있다고 가정합니다.

개별 Connection은 다음과 같은 조합으로 구분됩니다.

Client IP
Client Port
Server IP
Server Port

따라서 TIME_WAIT가 존재한다고 해서 Server의 TCP 443 Listening Socket 자체를 다른 Client가 사용할 수 없게 되는 것은 아닙니다.

Port 고갈 문제는 어느 Endpoint가 Active Close를 하고 어떤 주소·Port 조합을 대량으로 사용하는지를 함께 봐야 합니다.


11장 CLOSE_WAIT가 많을 때가 더 위험한 경우가 있다#

11.1 상대는 이미 연결을 끝냈다#

CLOSE_WAIT가 있다는 것은 상대방의 FIN이 이미 도착했다는 뜻입니다.

Remote
FIN
↓
Local TCP Stack
ACK
↓
CLOSE_WAIT

이제 남은 것은 Local Application이 Socket을 닫는 것입니다.


11.2 Application이 close하지 않으면 Resource가 남는다#

Application Code가 잘못되어:

Connection 종료 감지
↓
예외 발생
↓
close() 실행 안 됨

같은 상황이 반복되면 Socket이 계속 남을 수 있습니다.

결과적으로:

CLOSE_WAIT 증가
↓
File Descriptor 증가
↓
Memory 사용 증가
↓
새 Socket 생성 실패
↓
새로운 Client 연결 장애

로 이어질 수 있습니다.

이것이 실제 Connection Leak에 가까운 상황입니다.


12장 Connection Leak은 무엇인가#

Connection Leak은 Application이 더 이상 필요하지 않은 Network Connection을 적절히 정리하지 않아 Resource가 계속 남는 문제를 말합니다.

대표적인 원인은:

예외 처리 누락

Socket close 누락

Thread 정리 실패

Connection Pool 반환 실패

Library 사용 오류

비정상적인 상태 관리

등입니다.


12.1 TCP 상태만 보고 Leak이라고 단정하지 않는다#

다음 상태가 많다고 무조건 Leak은 아닙니다.

TIME_WAIT

반면:

CLOSE_WAIT가 장시간 지속
+
File Descriptor 증가
+
Application Memory 증가

같은 증거가 함께 있다면 Application Resource Leak을 더 강하게 의심할 수 있습니다.


13장 RST는 FIN과 무엇이 다른가#

13.1 FIN은 정상적인 종료 의사 표시다#

FIN:

"나는 이 방향의 전송을 정상적으로 마치겠다."

라고 볼 수 있습니다.


13.2 RST는 즉각적인 Connection Reset이다#

RST는 Reset입니다.

개념적으로:

현재 Connection 상태를
정상 FIN 절차 없이
즉시 중단

하는 데 사용됩니다.

Packet Capture에서는:

Flags [R]

또는:

Flags [R.]

같이 보일 수 있습니다.


14장 RST는 언제 발생할까#

RST는 다양한 상황에서 발생할 수 있습니다.

14.1 Listen하지 않는 Port에 연결#

Client:

Server:6000
SYN

Server에 해당 Port를 Listen하는 Service가 없다면 운영체제가 RST를 보낼 수 있습니다.

SYN
──────→

RST
←──────

14.2 Application이 Connection을 강제로 중단#

Application의 종료 방식이나 Socket Option에 따라 정상적인 FIN이 아니라 RST가 나타날 수도 있습니다.


14.3 존재하지 않는 Connection에 Segment가 도착#

TCP Stack이 현재 유효하지 않은 Connection에 대한 Segment를 받았을 때도 조건에 따라 RST가 발생할 수 있습니다.


14.4 중간 Network 장비가 Reset을 생성하는 경우#

일부 Firewall이나 Load Balancer 등이 정책에 따라 TCP RST를 생성하는 경우도 있습니다.

따라서:

RST 발견
=
Server Application이 보냈다

라고 바로 판단하면 안 됩니다.

누가 RST를 보냈는지 확인하는 것이 중요합니다.


15장 갑작스러운 장비 전원 OFF는 항상 RST를 보내는가#

아닙니다.

예를 들어 Controller의 전원이 갑자기 꺼졌다고 생각해보겠습니다.

전원이 사라졌기 때문에 장비가:

RST 전송

할 기회 자체가 없을 수 있습니다.

상대 Server는 당분간 Connection이 살아 있다고 생각할 수도 있습니다.

Server
ESTABLISHED

Controller
전원 OFF

이런 상태는 Application Traffic이나 TCP Keepalive, 별도의 Timeout 등에 의해 나중에 감지될 수 있습니다.

따라서:

비정상 종료
=
항상 RST

라고 이해하면 안 됩니다.


16장 kill -9를 하면 항상 RST가 발생하는가#

이 역시 단정하면 안 됩니다.

Process가 강제 종료되면 운영체제가 해당 Process의 Socket을 정리하게 되지만 실제 Wire상에서:

FIN

RST

아무 Packet 없음

중 무엇이 나타날지는 Socket 상태와 운영체제 동작, 설정 등에 따라 달라질 수 있습니다.

따라서 실습에서:

kill -9를 하면 RST가 나온다.

를 정답처럼 외우는 것은 적절하지 않습니다.

실제 Packet Capture로 확인해야 합니다.


17장 정상적인 종료 Packet을 직접 읽어보자#

예를 들어 Client가 먼저 종료한다고 가정합니다.

10.0.0.10:45000
→
10.0.0.5:6000

단순화한 Capture는 다음과 비슷할 수 있습니다.

10.0.0.10.45000 > 10.0.0.5.6000
Flags [F.]
seq 1000
ack 2000

10.0.0.5.6000 > 10.0.0.10.45000
Flags [.]
ack 1001

10.0.0.5.6000 > 10.0.0.10.45000
Flags [F.]
seq 2000
ack 1001

10.0.0.10.45000 > 10.0.0.5.6000
Flags [.]
ack 2001

핵심만 보면:

FIN
↓
ACK
↓
FIN
↓
ACK

입니다.


18장 실제로는 FIN과 ACK가 합쳐질 수도 있다#

항상 정확히 네 Packet으로 나타나는 것은 아닙니다.

상대방이 FIN을 받고 자신도 바로 종료할 준비가 되어 있다면:

FIN + ACK

를 한 Segment에서 보낼 수도 있습니다.

예:

Client                         Server

FIN
─────────────────────────────→

                    FIN + ACK
←─────────────────────────────

ACK
─────────────────────────────→

따라서 Packet 분석에서:

반드시 네 Packet이어야 정상

이라고 판단해서는 안 됩니다.


19장 ss로 TCP 상태 확인하기#

Linux에서는 ss를 이용해 TCP Socket 상태를 확인할 수 있습니다.

예:

ss -tan

특정 Port를 확인하려면:

ss -tan | grep ':6000'

처럼 사용할 수 있습니다.


19.1 Process 정보까지 확인하기#

권한이 있다면:

ss -tanp

를 사용해 Socket과 관련 Process를 함께 확인할 수 있습니다.

예:

ESTAB
10.0.0.5:6000
10.0.0.10:45000

CLOSE-WAIT
10.0.0.5:6000
10.0.0.11:45001

TIME-WAIT
10.0.0.5:6000
10.0.0.12:45002

이런 상태를 볼 수 있습니다.


20장 TCP 상태는 개수보다 추세를 본다#

한 번 조회해서:

TIME_WAIT 300개

를 봤다고 바로 장애라고 판단하면 안 됩니다.

다음과 같이 시간에 따른 변화를 보는 것이 더 중요합니다.

10:00
TIME_WAIT 120

10:05
TIME_WAIT 180

10:10
TIME_WAIT 110

10:15
TIME_WAIT 160

처럼 증가와 감소를 반복한다면 정상적인 Traffic 패턴일 수 있습니다.

반대로:

10:00
CLOSE_WAIT 100

10:05
CLOSE_WAIT 300

10:10
CLOSE_WAIT 700

10:15
CLOSE_WAIT 1500

처럼 계속 누적된다면 Application 처리를 확인해야 합니다.


21장 TIME_WAIT가 많은 이유를 먼저 찾는다#

TIME_WAIT를 무조건 줄이려고 Kernel Parameter부터 바꾸는 것은 좋지 않습니다.

먼저 질문해야 합니다.

왜 이렇게 많은 Connection을
짧게 만들고 닫고 있는가?

예를 들어 Application이 요청마다:

Connect
↓
Request
↓
Response
↓
Close

를 반복하고 있을 수 있습니다.

가능하다면 Protocol과 Application 특성에 따라 Connection 재사용을 검토할 수 있습니다.

예:

HTTP Keep-Alive

Connection Pool

Persistent TCP Connection

등입니다.


22장 TCP Keepalive와 Connection 종료는 다른 문제다#

TCP Keepalive는 오래 동안 Traffic이 없는 Connection에서 상대방이 여전히 존재하는지 확인하는 데 사용할 수 있는 기능입니다.

개념적으로:

ESTABLISHED
↓
오랫동안 Traffic 없음
↓
Keepalive Probe
↓
상대 응답 여부 확인

입니다.


22.1 Keepalive가 Connection Leak을 자동으로 해결하는 것은 아니다#

Application이 이미 상대의 FIN을 받아:

CLOSE_WAIT

상태인데 Socket을 닫지 않고 있다면 Keepalive가 해결책이 아닙니다.

CLOSE_WAIT는:

Application close 처리

가 필요한 문제입니다.

따라서:

CLOSE_WAIT 증가
→ Keepalive 짧게 설정

으로 해결하려는 접근은 적절하지 않을 수 있습니다.


23장 Application Timeout도 TCP Keepalive와 다르다#

예를 들어 주차관제 Protocol에서:

30초 동안 Heartbeat 없음
→ 장비 Offline 처리

라는 정책을 사용할 수 있습니다.

이것은 Application 수준의 상태 판단입니다.

TCP Keepalive는 TCP Stack 수준입니다.

Application Heartbeat
→ 업무 Application 상태 확인

TCP Keepalive
→ TCP Peer 생존 여부 확인 보조

서로 목적이 다릅니다.


24장 주차관제 사례 — Camera 재부팅 후 CLOSE_WAIT가 계속 증가한다#

다음 상황을 가정해보겠습니다.

Camera 100대
↓
TCP
↓
Image Server

Camera Firmware Update 후 여러 Camera가 순차적으로 재부팅되었습니다.

Server를 확인했더니:

CLOSE_WAIT 12
CLOSE_WAIT 48
CLOSE_WAIT 103
CLOSE_WAIT 231

처럼 계속 증가합니다.


24.1 Packet Capture를 확인한다#

Camera가 재부팅 전에 정상적으로 FIN을 보냈다고 가정하겠습니다.

Camera
FIN
──────→ Server

Server
ACK
←──────

Server TCP Stack은 정상적으로 FIN을 받았습니다.

그러나 Server Application이 해당 Socket을 닫지 않습니다.

결과:

Remote FIN 수신
↓
CLOSE_WAIT
↓
Application close() 없음
↓
CLOSE_WAIT 계속 유지

입니다.


24.2 이 경우 Network가 원인은 아닐 수 있다#

이 상태에서 Switch나 Cable을 교체해도 문제가 해결되지 않습니다.

확인해야 할 것은:

Socket 종료 Code

Exception 처리

Thread Lifecycle

Connection Manager

Library 사용 방식

입니다.

CLOSE_WAIT는 Network 문제와 Application 문제를 구분하는 좋은 단서입니다.


25장 주차관제 사례 — 장비 전원 OFF 후 연결이 오래 남는다#

이번에는 Gate Controller 전원이 갑자기 끊겼다고 가정합니다.

Controller는 FIN이나 RST를 보내지 못했습니다.

Server 입장에서는 즉시 알 수 없을 수 있습니다.

Server
ESTABLISHED

Controller
Power Off

Traffic이 없다면 이런 상태가 일정 시간 유지될 수도 있습니다.

이 경우:

Application Heartbeat

Read Timeout

TCP Keepalive

Device Status Monitoring

같은 구조가 장애 감지에 중요합니다.


26장 주차관제 사례 — RST가 계속 발생한다#

Packet Capture:

Controller → Server
SYN

Server → Controller
SYN/ACK

Controller → Server
ACK

Controller → Server
Data

Server → Controller
RST

이 경우 Server가 Connection을 강제로 종료한 것처럼 보입니다.

확인할 항목은:

Server Application Log

Protocol Parsing Error

Connection 정책

Idle Timeout

Firewall / Load Balancer

Server Restart 여부

등입니다.

RST 하나만 보고 Cable 문제라고 판단하면 안 됩니다.


27장 방화벽과 Load Balancer도 TCP 상태를 관리할 수 있다#

Stateful Firewall이나 Load Balancer는 TCP Connection 상태를 추적하는 경우가 많습니다.

예를 들어:

Client
↓
Firewall
↓
Server

구조에서 Firewall의 Session Timeout이 Server나 Client보다 짧을 수 있습니다.

오랫동안 Traffic이 없으면:

Firewall Session 제거

가 발생할 수 있습니다.

이후 Client가 기존 Connection이라고 생각하고 데이터를 보내면 예상하지 않은 장애가 발생할 수 있습니다.

따라서 장시간 유지되는 TCP Connection에서는:

Client Timeout

Server Timeout

Firewall Session Timeout

Load Balancer Timeout

을 함께 비교해야 합니다.


28장 FIN이 보이지 않는다고 반드시 이상한 것은 아니다#

Packet Capture 위치가 중간이라면 모든 Packet을 볼 수 있다는 보장이 없습니다.

예:

Mirror Port Drop

Capture 시작 전에 FIN 발생

다른 Routing Path 사용

NIC Offloading

Capture Filter 오류

등이 있을 수 있습니다.

따라서:

FIN이 Capture에 없다
=
상대가 FIN을 보내지 않았다

라고 무조건 단정해서는 안 됩니다.

필요하면 양쪽 Endpoint에서 확인합니다.


29장 정상 종료와 강제 종료를 비교해보자#

구분 정상 종료 강제 종료
대표 Flag FIN·ACK RST
목적 데이터 전송을 정상적으로 마침 Connection 즉시 중단
Half-Close 가능 일반적인 정상 절차 아님
상태 전이 FIN_WAIT·CLOSE_WAIT·TIME_WAIT 등 빠른 Reset
현장 의미 정상 Application 종료 가능 오류·정책·강제 종료 등 원인 확인 필요

RST가 있다고 반드시 장애라는 뜻은 아니지만 왜 Reset됐는지 확인해야 하는 중요한 단서입니다.


30장 TCP 상태별 현장 판단 기준#

상태 의미 오래 지속될 때 확인
ESTABLISHED 연결된 상태 실제 Traffic·Heartbeat 여부
FIN_WAIT_1 FIN 전송 후 ACK 대기 Packet Loss·상대 상태
FIN_WAIT_2 FIN ACK 후 상대 FIN 대기 상대 Application 종료 여부
CLOSE_WAIT 상대 FIN 수신, Local close 대기 Application Socket close
LAST_ACK Local FIN 후 ACK 대기 상대 응답·Network
TIME_WAIT 종료 후 일정 시간 상태 유지 Connection 생성률·Port 사용
LISTEN 연결 요청 대기 정상적인 Server 상태
CLOSED 연결 없음 정상

31장 tcpdump로 FIN과 RST 확인하기#

시험망이나 허가된 환경에서는:

tcpdump -n -i eth0 'tcp port 6000'

처럼 확인할 수 있습니다.

FIN을 포함한 Packet은 예를 들어:

Flags [F.]

처럼 보일 수 있습니다.

RST는:

Flags [R]

또는:

Flags [R.]

처럼 나타날 수 있습니다.


31.1 특정 Host까지 제한하기#

tcpdump -n -i eth0 'host 10.0.0.5 and tcp port 6000'

처럼 범위를 좁히면 불필요한 Traffic과 민감정보 수집을 줄일 수 있습니다.


32장 Wireshark에서는 무엇을 확인할까#

종료 문제를 분석할 때 다음 순서로 보면 좋습니다.

32.1 누가 먼저 FIN을 보냈는가#

Active Close를 시작한 Endpoint를 확인합니다.


32.2 FIN에 ACK가 돌아왔는가#

FIN Packet Loss 또는 Return Path 문제를 확인하는 데 도움이 됩니다.


32.3 상대방도 FIN을 보냈는가#

한쪽 Application이 Connection을 계속 유지하고 있는지 볼 수 있습니다.


32.4 RST가 있다면 누가 보냈는가#

Source IP와 Capture 위치를 확인합니다.

중간 장비가 생성했을 가능성도 고려해야 합니다.


32.5 마지막 Packet 이후 Socket 상태는 무엇인가#

Packet Capture와 ss 결과를 같은 시간에 비교하면 더욱 정확합니다.


33장 시험망에서 정상 종료를 관찰해보자#

33.1 간단한 TCP Server#

시험망에서:

nc -l 7000

처럼 Listener를 준비합니다.

nc 구현에 따라 Option은 다를 수 있습니다.


33.2 Client 연결#

nc 192.0.2.10 7000

으로 연결합니다.

동시에:

tcpdump -n -i eth0 'tcp port 7000'

을 실행합니다.


33.3 정상 종료 관찰#

Client를 정상적으로 종료하고:

FIN

ACK

FIN

ACK

흐름이 어떻게 나타나는지 확인합니다.

그리고 양쪽에서:

ss -tan

을 실행해 상태 변화를 관찰합니다.


34장 CLOSE_WAIT 실습은 Application Code로 이해하는 것이 좋다#

CLOSE_WAIT를 이해하려면 Server가 상대 FIN을 받은 뒤 Socket을 닫지 않는 상황을 만들 수 있습니다.

개념적인 흐름은:

Client 종료
↓
Server FIN 수신
↓
Server ACK
↓
Server Application은 Socket close 안 함
↓
CLOSE_WAIT 유지

입니다.

이 실습은 반드시 분리된 시험 환경에서 진행하고 실제 관제 Server에 의도적인 Resource Leak을 만들면 안 됩니다.


35장 Connection Leak을 진단할 때 함께 볼 지표#

Socket 상태만 보지 말고 다음을 함께 확인하는 것이 좋습니다.

TCP State별 개수

Process File Descriptor 수

Process Memory

Thread 수

Connection 생성률

Connection 종료률

Application Error Log

예를 들어:

CLOSE_WAIT 증가
+
FD 증가
+
Memory 증가

가 동시에 나타난다면 매우 중요한 증거입니다.


36장 무작정 TCP Kernel 설정부터 바꾸지 않는다#

장애가 발생했을 때 다음과 같이 접근하기 쉽습니다.

TIME_WAIT 많음
↓
TIME_WAIT 시간 줄이기

CLOSE_WAIT 많음
↓
Keepalive 짧게 만들기

하지만 이렇게 하면 원인을 가릴 수 있습니다.

먼저:

누가 Connection을 만드는가?

누가 먼저 닫는가?

FIN은 정상적으로 오가는가?

어떤 TCP State가 증가하는가?

Application이 close를 호출하는가?

를 확인해야 합니다.

설정 변경은 원인을 확인한 뒤 해야 합니다.


37장 TCP 종료와 Application Protocol을 연결해서 봐야 한다#

예를 들어 Server가 Response를 보내자마자 Connection을 닫는 Protocol이라면:

Request
↓
Response
↓
FIN

이 정상일 수 있습니다.

반대로 장시간 Persistent Connection을 유지하도록 설계된 장비에서 매 요청마다 Connection이 끊긴다면 비정상일 수 있습니다.

따라서:

FIN이 많다
=
장애

라고 할 수 없습니다.

Application Protocol이 Connection을 어떻게 사용하도록 설계되어 있는지를 알아야 합니다.


38장 TCP 종료 장애 진단 순서#

38.1 현재 TCP 상태 확인#

ESTABLISHED?

CLOSE_WAIT?

TIME_WAIT?

FIN_WAIT?

를 확인합니다.


38.2 Packet Capture 확인#

FIN을 누가 보냈는가?

ACK가 왔는가?

상대 FIN이 왔는가?

RST가 있는가?

를 확인합니다.


38.3 Application Log 확인#

Socket Close

Connection Error

Exception

Timeout

Process Restart

등을 확인합니다.


38.4 중간 장비 확인#

Firewall Session Timeout

NAT Session

Load Balancer

VPN

을 확인합니다.


38.5 Resource 상태 확인#

File Descriptor

Memory

Thread

Socket Count

Ephemeral Port

를 확인합니다.


39장 증상별로 원인을 좁혀보자#

증상 우선 확인
CLOSE_WAIT 지속 증가 Application close 누락
TIME_WAIT 많음 Connection 생성·종료 패턴
FIN_WAIT_1 장기 지속 FIN ACK 손실·상대 상태
FIN_WAIT_2 장기 지속 상대 Application이 FIN을 보내는지
RST 반복 Application·Firewall·Load Balancer
ESTABLISHED만 오래 남음 Heartbeat·Keepalive·Timeout 정책
새 Connection 실패 FD·Port·Memory·Listen Queue
장비 재부팅 후 오래된 연결 잔존 Keepalive·Heartbeat·Session Timeout

40장 자주 발생하는 오해#

40.1 TIME_WAIT는 Connection Leak이다?#

아닙니다.

TIME_WAIT는 정상 TCP 종료 과정에서 필요한 상태입니다.

과도한 개수가 실제 Resource 문제를 만드는지는 Connection 패턴과 Endpoint 역할을 함께 봐야 합니다.


40.2 CLOSE_WAIT는 Network가 Socket을 안 닫아서 발생한다?#

일반적으로 CLOSE_WAIT는 Local Application이 Socket 종료 처리를 기다리는 상태입니다.

오래 누적된다면 Application을 우선 확인해야 합니다.


40.3 장비가 비정상 종료되면 반드시 RST를 보낸다?#

아닙니다.

전원 장애처럼 Packet을 보낼 기회가 없는 경우 아무 종료 Packet도 보내지 못할 수 있습니다.


40.4 FIN을 보내면 바로 양방향 연결이 끝난다?#

아닙니다.

FIN은 해당 방향의 전송 종료를 의미하며 TCP는 Half-Close가 가능합니다.


40.5 RST가 보이면 공격이다?#

아닙니다.

닫힌 Port, Application 강제 종료, Firewall 정책 등 정상적이거나 운영상의 여러 이유로 RST가 발생할 수 있습니다.


41장 핵심 개념 한눈에 정리하기#

개념 핵심 의미
FIN 한 방향의 정상적인 데이터 전송 종료
ACK FIN 등 상대 Segment 수신 확인
RST Connection 즉시 Reset
Half-Close 한 방향만 먼저 종료된 상태
FIN_WAIT_1 FIN을 보내고 ACK 대기
FIN_WAIT_2 FIN ACK 후 상대 FIN 대기
CLOSE_WAIT 상대 FIN 수신 후 Local Application close 대기
LAST_ACK Local FIN 후 마지막 ACK 대기
TIME_WAIT 종료 후 일정 시간 Connection 정보 유지
Connection Leak Application이 불필요한 Socket Resource를 정리하지 못하는 문제
TCP Keepalive 유휴 Connection의 Peer 상태 확인 보조
Application Heartbeat 실제 Application의 응답 상태 확인

42장 자기 점검#

42.1 FIN은 무엇을 의미하는가#

해당 Endpoint가 그 방향으로 더 이상 보낼 데이터가 없다는 의미입니다.

반대 방향의 데이터 전송까지 즉시 종료한다는 뜻은 아닙니다.


42.2 CLOSE_WAIT가 계속 증가한다면 무엇을 먼저 확인할까#

Local Application이 상대의 FIN을 받은 뒤 Socket을 정상적으로 닫고 있는지 확인합니다.


42.3 TIME_WAIT는 왜 필요한가#

마지막 ACK가 유실됐을 때 재전송된 FIN에 다시 대응할 수 있고, 이전 Connection의 지연된 Segment가 새로운 Connection에 섞이는 위험을 줄이는 데 필요합니다.


42.4 RST가 보이면 무엇을 확인해야 할까#

먼저 누가 RST를 보냈는지 확인합니다.

그다음:

Application

Listening Port

Firewall

Load Balancer

Connection 상태

등을 확인합니다.


42.5 장비 전원이 갑자기 꺼졌는데 Server에 ESTABLISHED Socket이 남아 있는 것은 가능한가#

가능합니다.

장비가 FIN이나 RST를 보내지 못했다면 Server가 즉시 장애를 알지 못할 수 있습니다.

Application Heartbeat, Timeout 또는 TCP Keepalive 등이 이후 장애를 감지하는 데 사용될 수 있습니다.


43장 이 글을 마치며#

TCP Connection은:

SYN
↓
SYN/ACK
↓
ACK
↓
ESTABLISHED

로 시작하지만 종료는 단순히 Connection을 지우는 과정이 아닙니다.

정상적인 종료에서는:

FIN
↓
ACK
↓
FIN
↓
ACK

과 같은 과정이 나타날 수 있습니다.

그리고 그 과정에서 각 Endpoint는 서로 다른 상태를 거칩니다.

FIN을 먼저 보낸 쪽

ESTABLISHED
↓
FIN_WAIT_1
↓
FIN_WAIT_2
↓
TIME_WAIT
↓
CLOSED

상대 FIN을 먼저 받은 쪽은:

ESTABLISHED
↓
CLOSE_WAIT
↓
LAST_ACK
↓
CLOSED

처럼 진행할 수 있습니다.

따라서 현장에서 Socket 상태를 볼 때 단순히:

Socket이 많이 남아 있다.

고 판단해서는 안 됩니다.

먼저:

어떤 상태가 많은가?

누가 먼저 FIN을 보냈는가?

상대 FIN은 도착했는가?

Application이 close했는가?

RST는 누가 보냈는가?

Connection 생성 속도는 얼마인가?

Resource가 실제로 고갈되고 있는가?

를 확인해야 합니다.

특히 다음 네 가지를 기억하면 TCP 종료 문제를 훨씬 정확하게 판단할 수 있습니다.

FIN은 Connection 전체의 즉시 종료가 아니라 한 방향의 정상적인 전송 종료를 의미합니다.

CLOSE_WAIT가 오래 누적되면 Local Application의 Socket 종료 처리를 우선 확인해야 합니다.

TIME_WAIT는 TCP의 정상적인 안전장치이며 존재 자체가 Connection Leak을 의미하지 않습니다.

RST는 강제적인 Connection Reset이지만 원인은 Application·운영체제·Firewall·Network 장비 등 다양할 수 있습니다.

TCP 종료 장애를 제대로 분석하려면 결국:

Packet
+
TCP State
+
Application Log
+
System Resource

를 같은 시간축으로 연결해야 합니다.

이 네 가지를 함께 보면 연결이 안 끊긴다, 새 연결이 안 들어온다, RST가 계속 발생한다 같은 모호한 증상을 실제 원인으로 좁혀갈 수 있습니다.

이 페이지의 목차