출처 : KLDP 답글 중… (http://kldp.org/node/165#comment-401)
예전에 제가 TIME_WAIT 상태에 대해 정리했던 글입니다. 한번 읽어보시길 바랍니다.
Unix나 Linux 상에서 네트워크 프로그래밍을 하실때는 W.Richard Stevens가 저술한 Unix Network Programming을 참고하시면
많은 도움이 됩니다.(유닉스 네트워크 프로그래밍 서적의 바이블이죠.)
TCP는 여러개의 State를 가질수 있습니다. 예를들어 CLOSED(연결이 닫혔을때) LISTEN(연결을 기다리고 있을때), ESTABLISHED(연결이 되었을때) 등등의 상태를 가질수 있습니다. 이중에서 TIME_WAIT라는 상태가 있는데, 이해하기 가장 어려운 부분인것 같습니다. TIME_WAIT 상태는 다음과 같은 경우에 발생합니다. 우선 Client와 Server가 TCP로 연결이 되어 있다고 가정합시다. 이때 클라이언트가 연결을 끊으려면 close함수를 호출합니다. 이 함수를 호출하면 서버에 FIN segment를 보내게 됩니다. 그러면 서버는 이 메시지를 받고 클라이언트가 접속을 끊으려고 하는 것을 알게됩니다. 따라서 서버가 CLOSE_WAIT 상태가 되면서 클라이언트에게 ack segment를 보냅니다. 즉 “네가 접속을 끊는 다는 신호를 받았다” 이런 의미입니다. 이 메시지를 받으면 클라이언트는 FIN_WAIT_2 상태가 됩니다. 이 상태에서 서버는 자신의 socket을 close하고 다시 클라이언트에게 FIN segment를 보냅니다. 즉 자신도 연결을 닫았다는 신호를 클라이언트에게 보내는 것입니다. 이 메시지를 받으면 클라이언트는 ack segment를 보내면서 TIME_WAIT 상태가 됩니다. 즉 서로간의 확인하에 완전히 연결이 끊기게 됩니다. 근데 이 상태에서 곧바로 CLOSED 상태가 되는 것이 아니라 2 MSL(maximum segment lifetime – 1분~4분) 동안 TIME_WAIT상태를 유지합니다. 왜 곧바로 CLOSED 상태가 되지 않고 일정시간 동안 TIME_WAIT 상태가 되는 것일까요?
뭔가 이상하지 않습니까? 그 이유는 다음과 같습니다. TCP는 신뢰성을 보장해 주는 프로토클 입니다.(UDP는 아니지요.) 따라서 연결시에도 신뢰성을 보장하기 위하여 Three-way handshake라는 기법을 사용하여 연결을 하고 종료시에도 위와같이 복잡한 과정을 거쳐 서로가 close된것을 확인하게 되는 것입니다. TIME_WAIT 상태도 이와같은 신뢰성 보장을 위한 한가지 방법이라고 보시면 됩니다.
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.
흠 첫번째는 신뢰성있는 연결 종료를 위한 것이라고 쓰여있습니다. 근데 두번째는 무엇일까요?
network에 있는 duplicate segments들이 소멸시키기 위해서?? 무슨 소리인지 -_-;;
이 두가지에 대하여 자세히 설명해 보겠습니다.
먼저 첫번째에 대해서 설명하겠습니다.
이것은 말그대로 신뢰성있은 연결 종료를 위한것입니다. 다음과 같은 상황을 가정합시다. 클라이언트가 FIN_WAIT_2 상태에서 서버의 FIN segment를 받으면 TIME_WAIT상태가 되면서 서버에 ack segment를 보낸다고 하였습니다. 하지만 이 ack segment가 네트워 크의 오류로 인해 서버에 도착하지 못할수도 있습니다. 그런 경우라면 서버에서는 일정시간 이후에 다시 클라이언트에게 FIN segment를 보냅니다. 자신이 응답을 못받았으니 다시 보내달라는 의미지요. 만약 클라이언트가 CLOSED 상태 즉 연결이 닫혀 있는 상태에서 서버가 다시 보낸 이 FIN segment를 받으면 어떻게 될까요? 그런 경우라면 ack대신 RST라는 segment를 보내게됩니다. 즉 CLOSED 되면서 이전에 서버와 연결했던 정보들이 전부 없어졌으므로 서버가 다시 요청을 하면 “나는 너를 모른다. 왜 이상한 메시지를 보내느냐?” 하면서 서버가 원하는 ack 대신 RST라는 segment를 보내게 되는 것입니다. 이런 상황을 방지하기 위하여 일정시간 동안 TIME_WAIT라는 상태를 유지하여 서버가 다시 FIN을 보냈을때 대답할수 있게 해줍니다.
두번째에 대한 설명입니다.
다음과 같은 상황을 가정해 봅시다.
우선 현재 111.111.111.111:23 번하고 111.111.111.112:1500번이 연결되어 있다고 합시다.둘이 막 패킷을 주고 받다가 둘이 연결을 정상적으로 끊었다고 가정합시다. 그 후에 둘이 곧바로 연결을 해서 방금전과 마찬가지로 111.111.111.111:23 과 111.111.111.112:1500으로 연결되었다고 합시다. 근데 여기서 문제가 발생하였습니다. 바로 이전에 연결이 성립되었을때 111번에서 112번으로 보낸 패킷하나가 라우터의 오류로 인터넷을 뺑뺑이 돌다가 이전 연결이 끊어지고 지금 새로운 연결이 되었을때 112번에 도착했습니다. 무슨 소리냐구요? 예를 들어 라우터 A와 B가 있습니다. 근데 라우터의 일시적인 오류로 라우터 A의 데이타가 B로 전송하면 B가 다시 A로 그 데이타를 전송하여 순간적인 루프에 빠질수가 있을 수도 있습니다. 이렇게 루프에 빠지던 놈이 이전 연결이 끝나고 새로운 연결이 생겼을 때 도착한다면 문제가 발생하겠죠? 즉 원하지 않는 데이타가 전송되었으니 오류가 발생할 수 도 있는 것입니다. 이것을 방지하기 위하여 TIME_WAIT 상태를 둡니다. TIME_WAIT상태에 있는 동안은 같은 연결이 발생하지 못하도록 방지합니다. 즉 이전에 내가 연결되었던 포트가 1500번이라면 다음 연결은 현재 1500번이 TIME_WAIT이므로 1501번으로 연결되게 됩니다. 그렇게 하면 위와같은 문제를 해결할 수 있습니다. 보통 TIME_WAIT상태는 2 MSL입니다. 즉 인터넷 상에서 패킷이 존재하는 시간보다 길게 설정됩니다. 그러므로 TIME_WAIT상태가 끊나면 인터넷 상에서는 이전 연결에 보내졌던 패킷이 모두 소멸되었다고 확신할 수 있으므로 새로운 연결을 만들어도 문제가 발생하지 않는것입니다..