서버 프로그래밍

TCP Note #2 Gracefully Close(..)

하늘흐늘 2009. 5. 13. 17:58
반응형

From Chapter 6 내부구조, TCP/IP The Pocket Guide to TCP/IP Sockets C,
MSDN closesocket function[winsock], 'Graceful Shutdown, Linger Options, and Socket Closure',
NetworkProgramming For Microsoft Windows




What?
Gracefully Close란 TCP에서 4way-handshake를 정상으로 마친 그러니까 서로 FIN, ACK를 주고 받고 연결이 종료된 상태이다. 

Why Important?
이 Gracefully Close를 통하여 소켓의 close(..)를 호출한 쪽은 자신이 보내고자 하는 데이터가 정상적으로 상대편에 다 도착했음을 보장받는다. 이는 TCP가 신뢰성 있는 프로토콜이며 정상적인 Gracefully Close는 내 Send Queue에 있는 데이터가 다 보내진 후에 FIN(종료메시지)를 보내기 때문이다. 덧붙여서 내가 소켓을 close(..)한다는 의미는 더 이상 소켓을 쓰지 않겠다는 말이 되니 내가 상대방으로 부터 못 받는 데이터가 있다는 것은 큰 의미를 지니지는 않을 수도 있다. 

How is it checked?
 기본적으로 Gracefully Close는 close(..) 함수로 소켓을 닫았을 떄 일어나는 동작이다. 기본적인 동작은 close(..)가 소켓을 닫고 바로 사용자에게 리턴되며 내부적으로는 Gracefully Close 동작이 일어나게 된다. 여기서 문제의 핵심은 내부적으로 일어나는 Gracefully Close가 정상적으로 일어나지 않고 Aborted, 즉 Connection Reset이 일어날 수도 있다는 것이다. 이는 내 Send Queue에 미쳐 보내지 못한 데이터가 남아있을 수도 있다는 것이다.
 민감한 프로그램에서는 Gracefully Close를 체크하여 완전성을 보장받기를 원할 것이다. 이 Gracefully Close를 수행을 보장하는 방법은 2가지가 있으며 첫번째로 가장 쉬운 방법은 Linger옵션을 사용하는 것이다.
※linger구조체는 l_onoff, l_linger라는 멤버변수를 가지고 있으며 l_onoff는 0이 off값이며 0이외의 값이 on값이 되고 l_linger는 time-out값을 지정하기 위하여 사용된다.
 기본 close(...)의 동작은 linger구조체의 l_onoff를 off(0)로 지정한 것과 같다. linger구조체의 l_onoff를 on(0 이외의값)으로 지정한 경우는 l_linger값에 따라 동작이 달라진다.
 1) l_linger값이 0일 경우 Gracefully Close가 일어나는 것이 아니라 Hard 혹은 Abortive Close라고 불리우는 Connection Reset이 일어난다. 즉, 보내지 못한 모든 데이터는 즉각 폐기되며 컨넥션을 즉시 종료된다.  
 2) l_linger값이 0이 아닐 경우에는 Gracefully Close를 수행하며 close(..)는 Gracefully Close가 일어나거나 혹은 time-out이 일어날 동안 기다리다가 리턴한다. 여기서 time-out이 일어날 경우 OS는 내부적으로 Abortive Close를 수행한다. Win32프로그래밍의 주의할 점은 closesocket()으로 linger옵션을 지정하여 Gracefully Close를 체크하여도 Gracefully Close가 정상 작동했는지를 알 수 없다는 것이다. 즉, closesocket(..)함수가 에러를 리턴하지 않는다는 사실이다. Unix와 같은 경우 Linger옵션이 지정된 경우 Gracefully Close가 일어나지 않고 time-out이 일어난 경우 ETIMEDOUT 에러를 리턴한다.

"If the data is sent before the timeout specified in the l_linger member expires or if the connection was aborted, the closesocket function won't return an error code (the return value from the closesocket function is zero)."  In closesocket Function, MSDN...

 Gracefully를 수행하는 2번쨰 방법은 Gracefully Close가 필요하지 않게 Application Protocol을 만들어 Application Level에서 마지막으로 모든 데이터를 수신했음을 확인한 쪽에서 closesocket(..)을 호출하는 것이다. 뭐, 예를 들자면 echo예제와 같은 경우에 echo를 Send한 후 Recv한 후 클라이언트 쪽에서 소켓을 close하는 방법이 있겠다. 이 예제에서 소켓을 close하는 시점은 서버와 클라이언트 사이에서 더이상 echo가 일어나지 않는다는 것을 보장 받는다.
 
이 2번쨰 방법으로 MS가 공식적으로 추천하는 방법이 있다. closesocket에 의한 불명확한 Gracefully Close를 해결하는 방법으로 이 방법은 Graceful Shutdown, Linger Options, and socket Closure라는 글에 나와 있는 방법이며 처리 로직은 아래와 같다.
[참고로 shutdown을 2번쨰 인자로 SD_SEND와 함께 호출할 시 상대편에 FIN패킷이 전송됩니다.]
(1) Client Side : invoke shutdown(s, SD_SEND)
(2) Client Side : invoke recv(..)
(2) Server Side: receives FD_CLOSE
(3) Server Side: sends any remaining response data
(4) Server Side: invoke shutdown(s, SD_SEND)
(5) Server Side: invoke closesocket(..)
(6) Client Side : recv FD_READ and FD_CLOSE
(7) Client Side : invoke closesocket(..)


※closesocket(..)시 주의점
1) Overlapped I/O

 - Pending 되어있던 작업들은 자동으로 Cancel되는 관계로 인위적으로 해당 작업을 cleanup할 필요는 없다.
 - Pending 되어있던 작업들은 WSA_OPERATION_ABORTED 에러를 호출할 것이다.

2) Non-Blocking I/O
 - LINGER옵션을 사용하지 않을 것을 권장한다.
 - closesocket 호출이 즉시 처리되지 못한다면 WSAEWOULDBLOCK을 호출할 것이며 이럴 경우 closesocket 호출이 성공할 때까지 재차 호출해야 한다.

반응형