#keywords Network,TCP,TIME_WAIT [[TableOfContents]] TCP의 각각의 상태의 의미를 ["RFC 793"]의 TCP 기본 연결, 종료 과정을 통해 알아본다. == 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 }}} attachment:transport-fig-059-c.png ''* 구글에서 적당히 가져온 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''': 호스트 간에 상호 연결이 된 상태. 이 상태에서 데이터를 주고받을 수 있음. == 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 }}} attachment:TIMEWAIT.jpg ''* 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 상태가 표시된다. === TIME_WAIT 상태를 유지하는 이유 === TIME_WAIT가 필요한 이유는 2가지가 있다. 《Unix Network Programming》에서는 TIME_WAIT 상태가 있는 이유에 대해 다음과 같이 설명한다. 1. to implement TCP's full-duplex connection termination reliably, and 1. 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 공격을 받고 있을 수 있다.). {{{+1 TIME_WAIT를 없애는 방법 }}} 기술적으로 없애는 방법만 간단히 소개한다. 소켓 옵션 중 linger option을 off 시키면 연결 종료 시 TIME_WAIT 상태를 거치지 않고 CLOSED 상태로 바뀌게 된다. TIME_WAIT가 너무 많아 문제가 생기는 경우는 별로 없다. 이를 적용하면 오히려 통신이 불안정해질 수 있고 그 이유는 위에서 설명했다. {{{#!c /* 중략 */ 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)) }}} == 기타 상태 == '''CLOSING''': 연결은 종료되었으나 전송 도중 데이터가 분실된 상태. '''UNKNOWN''': 소켓의 상태를 알 수 없음. 솔라리스에서는 다음 2개의 상태를 더 표시한다. '''IDLE''': 소켓이 열렸지만 binding 되지 않은 상태 '''BOUND''': listen이나 연결을 위한 준비 상태 참조 ---- http://www.ietf.org/rfc/rfc0793.txt ---- CategoryDocument