TCP 상태(CLOSE_WAIT, TIME_WAIT)
트래픽을 만들어내는 어떤 툴을 사용하다가 CLOSE_WAIT 상태로 계속 유지되는 버그를 마주쳤다. 그런 의미에서 TCP 상태에 대해서 공부하고 정리한다.
TCP State
먼저 CLOSE_WAIT, TIME_WAIT가 어디서 나오는 용어인지 알아보자.
상태 | 설명 |
---|---|
CLOSE | 커넥션 없음 |
LISTEN | Passive open, SYN을 기다리는 상태 |
SYN-SENT | SYN을 보내고 ACK를 기다리는 상태 |
SYN-RCVD | SYN+ACK을 보내고 ACK를 기다리는 상태 |
ESTABLISHED | 커넥션이 생성된 상태, 데이터를 전송할 수 있다. |
FIN-WAIT-1 | 첫 FIN이 보내진 상태, ACK를 기다리고 있다. |
FIN-WAIT-2 | 첫 FIN에 대한 ACK를 받은 상태, 2번째 FIN을 기다리고 있다. |
CLOSE-WAIT | 첫 FIN을 받고 ACK를 보낸 상태, 어플리케이션의 종료를 기다리고 있다. |
TIME-WAIT | 2번째 FIN을 받고 ACK를 보낸 상태, 동일 포트와 주소에 커넥션이 생성되지 않도록 하는 시간(2MSL time out)을 기다리는 상태 |
LAST-ACK | 2번쨰 FIN을 보내고 ACK를 기다리는 상태 |
CLOSING | 양쪽이 동시에 닫기로 한 상태 |
TCP Connection open
TCP에서 connection established 까지 가는 과정을 그림으로 그렸다. Iperf라는 툴을 사용해서 Client로부터 Server로 100M의 트래픽을 전송하는 것을 대상으로 한다.
각 TCP state가 established 되면 클라이언트에서는 데이터를 전송한다.(100M)
TCP connection close
TCP 연결 해제 과정에서의 TCP 상태에 대해 알아보자
TCP 커넥션 종료는 클라이언트와 서버 어느 쪽에서도 할 수 있기 때문에, 클라이언트와 서버로 나누지 않고 active close와 passive close로 나눈다. 여기서 말하는 Active close와 Passive close는 다음과 같다.
- Active close: TCP 연결 해제 요청한 쪽, 그러니까 트래픽을 전송(request)하고 정상적으로 전송 되었으면 연결을 끊는 쪽
- Passive close: TCP 연결 해제 요청을 받은 쪽, 트래픽을 받는 쪽(api 서버라고 치면 클라이언트에게 응답을 주는 쪽)
이 포스팅에서는 Client가 Active Close, Server가 Passive Close라고 생각한다.
Active close
- 클라이언트는 FIN을 전송하고 FIN-WAIT-1 상태로 전환
- 클라이언트는 FIN에 대한 ACK를 수신하고 FIN-WAIT-2 상태로 전환
- 클라이언트는 FIN을 수신하면 ACK를 전송하고 TIME-WAIT 상태로 전환
- TIME-WAIT 상태로 2MSL 동안 남아있는다.
- 타이머가 만료되면 클라이언트는 CLOSED 상태가 된다.
FIN-WAIT-2 상태에서는 일정시간이 지나면 TIME-WAIT로 전환된다.
Passive close
- 서버는 FIN을 수신하고 ACK를 보낸다. CLOSE-WAIT 상태로 전환
- 프로세스로부터 passive close 명령을 받으면 서버도 FIN을 전송한다. LAST-ACK 상태로 전환
- LAST-ACK 상태로 있다가 클라이언트로부터 온 마지막 ACK를 수신하면 CLOSED 상태가 된다.
아래 그림을 보면 connection open 부터 close 까지의 TCP 상태에 대해 알 수 있다.
위 그림에서 server 쪽에서 passive close를 받고(또는 받지 못하거나) fin을 정상적으로 보내지 못하면 Server쪽은 영원히 CLOSE_WAIT 상태로 기다리게 된다.(Client의 FIN-WAIT-2는 일정 시간이 지나면 TIME WAIT로 바뀐다)
CLOSE WAIT
FIN-WAIT는 일정 시간이 지나면 TIME-WAIT 상태로 전환 되고, TIME-WAIT는 재 사용이 가능한 상태지만 CLOSE WAIT는 포트를 잡고 있는 프로세스의 종료, 네트워크 재시작 외에는 제거할 방법이 없다.(그래서 어플리케이션의 정상 종료가 필요하다.)
나도 어떤 툴(iperf2 server를 daemon으로 실행)을 사용했을 때 트래픽을 받는 쪽에서 CLOSE_WAIT 상태로 계속해서 포트가 잡혀있는 상황이 발생했고, 이 CLOSE_WAIT 상태가 계속 쌓인다면 특정 개수 이상으로 쌓이면 서버가 정상적으로 동작하지 못할 것으로 보인다. 서버쪽에서 FIN을 수신하고 ACK를 보냈지만, 서버 프로세스로부터 passive close 명령을 받지 못해서 FIN을 전송하지 못하고 CLOSE_WAIT 상태로 유지되었다. CLOSE_WAIT 상태는 위에서 말했듯 프로세스에서 정상 종료가 되지 않으면, timeout도 없어서 다른 상태로 변경할 수도 없는 상태이기 때문에 이 프로세스의 종료 또는 네트워크를 재시작하는 것 아니면 제거가 불가능하다. 나는 iperf의 버전을 3.~ 으로 올려서 해결했다.
TIME WAIT
FIN WAIT 상태는 일정 시간이 지나면 TIME WAIT로 바뀌고, TIME_WAIT 상태는 2MSL동안 유지되고 2MSL 이 지나면 CLOSED 상태로 되고 커넥션이 닫힌다.
Active Close 쪽의 TIME WAIT는 2MSL(2분 정도)의 시간이 지나면 CLOSED 되기 때문에, 영원히 port를 잡고있는 사태는 없을 것이다.
참고자료
- 데이터 통신과 네트워크 - Chapter 24. 전송층 프로토콜
- 카카오 기술 블로그
댓글남기기