TCP의 각각의 상태의 의미를 RFC 793의 TCP 기본 연결, 종료 과정을 통해 알아본다.

1. TCP 연결 관련 상태 #

* RFC 793 문서에 나온 기본적인 TCP 연결 과정
      TCP A                                                      TCP B

  1.  CLOSED                                                     LISTEN
  2.  SYN-SENT    --> < SEQ=100>< CTL=SYN>                   --> SYN-RECEIVED
  3.  ESTABLISHED <-- < SEQ=300>< ACK=101>< CTL=SYN,ACK>     <-- SYN-RECEIVED
  4.  ESTABLISHED --> < SEQ=101>< ACK=301>< CTL=ACK>         --> ESTABLISHED
  5.  ESTABLISHED --> < SEQ=101>< ACK=301>< CTL=ACK>< DATA>  --> ESTABLISHED
transport-fig-059-c.png
[PNG image (54.18 KB)]

* 구글에서 적당히 가져온 3-way Handshake 이미지


LISTEN: 호스트가 요청을 받을 수 있도록 연결 요구를 기다리는 상태. 즉 포트가 열려있음을 의미. HTTP(80), MAIL(25), FTP(21), TELNET(23) 등.
SYN_SENT: 호스트가 SYN 패킷을 보내고(연결 요청), 상대방의 SYN+ACK 패킷을 기다리는 상태.
SYN_RECV: 호스트가 연결 요청을 받아 SYN+ACK 패킷으로 응답을 보내고, ACK 패킷을 기다리는 상태. 윈도우와 솔라리스에서는 SYN_RECEIVED로, FreeBSD는 SYN_RCVD로 표시한다.
ESTABLISHED: 호스트 간에 상호 연결이 된 상태. 이 상태에서 데이터를 주고받을 수 있음.


2. TCP 종료 관련 상태 #

* 정상적인 연결 종료 과정
      TCP A                                                   TCP B

  1.  ESTABLISHED                                             ESTABLISHED
  2.  (Close)
      FIN-WAIT-1  --> < SEQ=100>< ACK=300>< CTL=FIN,ACK>  --> CLOSE-WAIT
  3.  FIN-WAIT-2  <-- < SEQ=300>< ACK=101>< CTL=ACK>      <-- CLOSE-WAIT
  4.                                                         (Close)
      TIME-WAIT   <-- < SEQ=300>< ACK=101>< CTL=FIN,ACK>  <-- LAST-ACK
  5.  TIME-WAIT   --> < SEQ=101>< ACK=301>< CTL=ACK>      --> CLOSED
  6.  (2 MSL)
      CLOSED                       
TIMEWAIT.jpg
[JPG image (20.76 KB)]

* 4-way Terminate

3/4-way Terminate를 거쳐 종료된다. 3-way Terminate는 3-way Handshake와 비슷하게 FIN+ACK를 주고 받는다. 아직 호스트 B(passive close) 측에서 처리할 게 남았다면 ACK를 먼저 보내고 처리가 끝난 다음 FIN 전송. 이런 경우 4-way Terminate이라고 한다. 호스트 B가 바로 종료하지 않고 종료를 받았다는 ACK를 보내주기 위해 CLOSE_WAIT 상태가 필요하다. (TIME_WAIT는 뒤에 더 자세히 설명)

FIN_WAIT1: 호스트가 연결 종료를 요청하고, 상대방의 ACK 패킷을 기다리는 상태. 원격의 응답은 계속 받을 수 있다.
FIN_WAIT2: 호스트가 상대방의 연결 종료 요청을 기다리는 상태.
CLOSE_WAIT: 연결 종료 요청을 받고 연결이 종료되기까지 상대방의 데이터 전송을 기다리는 상태. 원격으로 부터 FIN+ACK 신호를 받고 ACK 신호를 원격으로 보냈다.
LAST_WAIT: FIN 패킷을 수신하고, 연결 종료에 대한 ACK 패킷을 보내고 나서, 상대방의 ACK 패킷을 기다리는 상태. LAST_WAIT 상태는 일반적으로 매우 짧은 시간 동안 유지되며, 클라이언트가 ACK 패킷을 보내는 속도에 따라 달라질 수 있다.
TIME_WAIT: 연결이 완전히 종료된 후에 원격의 수신 보장을 위해 일정 시간 기다리는 상태. 아래에 더 자세히 설명. FIN 패킷을 수신한 후, ACK 패킷을 보내고 CLOSED 하지 않고 일반적으로 2MSL(Maximum segment lifetime) 동안 이 상태를 유지한다. (Apache에서 KeepAlive를 OFF로 해둔 경우, Tomcat 서버를 쓰는 경우 등에서 이 상태를 자주 볼 수 있다.)
CLOSED: 완전히 연결이 종료된 상태.

  • 종료 요청을 한 호스트(active close)에서는 FIN_WAIT1, FIN_WAIT2, TIME_WAIT 상태가,
    종료 요청을 받은 호스트(passive close)에서는 CLOSE_WAIT, LAST_WAIT 상태가 표시된다.


2.1. TIME_WAIT 상태를 유지하는 이유 #

TIME_WAIT가 필요한 이유는 2가지가 있다. 《Unix Network Programming》에서는 TIME_WAIT 상태가 있는 이유에 대해 다음과 같이 설명한다.

  1. to implement TCP's full-duplex connection termination reliably, and
  2. to allow old duplicate segments to expire in the network.

첫 번째 이유는 원격의 종료까지 확인하여 신뢰성 있는 연결 종료를 위한 것이고, 두 번째 이유는 만료된 연결의 패킷 제거를 위해서인데, 종료한 소켓이 TIME_WAIT 상태에서 금방 사라지지 않는 이유가 두 번째 이유 때문이다. 이런 상황을 가정할 수 있다. 둘이 패킷을 주고 받다가 정상적으로 연결을 끊었다. 그리고 둘이 곧바로 연결을 해서 방금 전과 같은 포트로 연결되었다. 여기서 문제가 발생하는데 이전에 연결이 되었을 때 보낸 패킷이 라우터의 일시적인 오류로 네트워크를 뱅뱅 돌다가 다시 새로운 연결이 되었을 때 도착할 수 있다. 즉, 모든 패킷 순서를 엄격히 보장하는 TCP에서 원하지 않는 데이터가 수신되었으니 네트워크 오류가 발생할 수 있다. 이 때 TIME_WAIT 상태를 유지하면 같은 포트를 다른 프로세스가 다시 이용하는 것을 막는다. 같은 연결이 발생하지 못하도록 방지한다. 그리고 TIME_WAIT 상태는 2 MLS 시간 동안 유지한다. 즉, 네트워크에 패킷이 존재하는 시간보다 두 배 길게 설정된다. 따라서 TIME_WAIT 상태가 끝나면 네트워크 상에는 이전 연결에 보내졌던 패킷이 모두 소멸되었다고 확신할 수 있으므로 새로운 연결을 만들어도 문제가 발생하지 않을 것이다.

  • TIME_WAIT로 인해 부하가 걸리지는 않는다. 실제로 웹 서버를 운영하면 TIME_WAIT 상태가 수십, 수백 개 생기기도 한다. 수십 개 정도는 일반적인 수준이다.
  • SYN_RECV 상태가 너무 많다면 문제가 생길 수는 있다(DoS 공격을 받고 있을 수 있다.).


TIME_WAIT를 없애는 방법
기술적으로 없애는 방법만 간단히 소개한다. 소켓 옵션 중 linger option을 off 시키면 연결 종료 시 TIME_WAIT 상태를 거치지 않고 CLOSED 상태로 바뀌게 된다. TIME_WAIT가 너무 많아 문제가 생기는 경우는 별로 없다. 이를 적용하면 오히려 통신이 불안정해질 수 있고 그 이유는 위에서 설명했다.

/* 중략 */
int sock
struct linger   ling;

ling.l_onoff = 1;
ling.l_linger = 0;      /* 0 for abortive disconnect */

/* 중략 */

setsockopt(sock, SOL_SOCKET, SO_LINGER, &ling, sizeof(ling))

3. 기타 상태 #

CLOSING: 연결은 종료되었으나 전송 도중 데이터가 분실된 상태.
UNKNOWN: 소켓의 상태를 알 수 없음.

솔라리스에서는 다음 2개의 상태를 더 표시한다.

IDLE: 소켓이 열렸지만 binding 되지 않은 상태
BOUND: listen이나 연결을 위한 준비 상태


참조


Retrieved from http://hyacinth.byus.net/moniwiki/wiki.php/TCP 연결 상태 의미
last modified 2023-04-11 18:42:55