티스토리 뷰
소켓의 우아한 연결 종료 Graceful shutdown
1. 사용 이유
: 중요 데이터의 소실 방지.
서버로부터 데이터를 전송 받는 도중 클라이언트가 소켓 종료를 시도 하였을 때, 서버와 클라이언트간 I/O 스트림 종료로 인한 데이터 소실을 막기 위해 사용된다.
또한, 서버에서 TIME_WAIT가 남는게 아닌, 클라이언트에서 TIME_WAIT가 남도록 하는것이며, 클라이언트가 먼저 소켓 종료 함수를 호출하도록 하는것이 핵심이다.
즉, TIME_WAIT가 발생하는 것은 정상적인 소켓 종료가 이루어지는 과정에서 자연스럽게 발생하는 과정이다.
추가적으로 TIME_WAIT / CLOSE_WAIT가 남았을 경우 남기지 않고 Linger 옵션을 사용하여 바로 소켓을 종료시키는 비정상종료(Abortive Shutdown)도 알아보도록 한다.
2. 4-ways-hand shake
: Initiator 를 클라이언트, Receiver를 서버로 가정.
ㄱ. 클라이언트에서는 세션 종료를 시작한다는 의미(shutdown)로 서버에 FIN 패킷을 전송하고 FIN_WAIT_1 상태가 된다. => 클라 : 나 종료해도 됨?
ㄴ. 서버에서는 FIN 패킷을 받고 클라이언트로 잘 받았다는 의미로 ACK 패킷을 클라이언트로 보낸 후 CLOSE_WAIT 상태로 돌입, 클라이언트는 FIN_WAIT_2 상태가 된다. => 서버 : ㄱㄷ.
ㄷ. 클라이언트로 부터 FIN 패킷을 받게된 서버는 종료에 필요한 작업을 실행하게 되며, 클라이언트로 보내야 하는 데이터가 출력 스트림에 남아있는지 확인 후 전부 보내고 난 다음 종료할 준비가 완료 되면 closesocket(linux : close) 함수를 호출하여 클라이언트에 FIN 패킷을 보낸다. 서버는 LAST_ACK 상태가 되고, 클라이언트는 TIME_WAIT 상태가 된다. => 서버 : ㅇㅇ 종료해도 됨.
ㄹ. 클라이언트는 서버로부터 FIN 패킷을 받게 되었다면 closesocket (linux : close)를 호출하여 소켓 I/O 스트림을 모두 종료하여 서버와 연결을 끊는다. => 클라 : 끊는다 ㅂㅂ
3. closesocket과 shutdown 함수
3.1 shutdown
int shutdown( int socket, int how ) |
|||
socket |
대상 소켓 |
||
how |
Linux | Window | 설명 |
SHUT_RD | SD_RECEIVE | 입력 스트림 종료 | |
SHUT_WR | SD_SEND | 출력 스트림 종료 | |
SHUT_RDWR | SD_BOTH | 입출력 스트림 종료 |
입력 스트림 종료 : 상대에게 더이상 데이터를 받지 않음
출력 스트림 종료 : 상대에게 더이상 데이터 전송하지 않음. 상대에게 더이상 보낼 데이터가 없다는 의미로 FIN 패킷 전송
입출력 스트림 종료 : 상대에게 더이상 데이틀 받지 않고 전송도 안함. 상대에게 더이상 보낼 데이터가 없다는 의미로 FIN패킷 전송
한번 shutdown된 소켓의 입출력 스트림은 재활용이 불가능하다.
또한, 해당 함수의 중요한 점은 송수신 함수가 동작하지 않도록 막는다는것이며, 송신의 경우 버퍼에 차있는 데이터가 상대쪽으로 넘어가는 것을 막는것은 아니다. 즉, 송신의 경우 송신 버퍼로 데이터를 복사할 수 없게 만들고, 수신의 경우 수신 버퍼의 데이터는 모두 비우고 수신 관련된 함수의 호출을 막는다.
출력 스트림을 종료하게 되면 상대에게 바로 FIN 패킷을 날림으로 4-way-hand shake를 유도하게 된다
이런 동작을 half-close라 하며 클라이언트가 자신이 보내고 싶은 데이터를 모두 보내고 종료할 수 있게 만들어준다.
=> Graceful shutdown
3.2 closesocket (Linxu : close)
: shutdown함수와 마찬가지로 입출력 스트림을 종료(FIN 패킷 전송)하지만, 해당 함수의 호출이 끝나는 시점에서 소켓 리소스를 반환하는 동작을 수행한다. 소켓 버퍼에 쌓인 데이터와 소켓의 옵션에 따라 동작이 다르다.
4. Linger 옵션
소켓이 종료될 때와 소켓 버퍼에 쌓인 데이터는 소켓의 옵션에 따라 동작이 다르며 이는 Linger 옵션을 통해 설정이 가능하다.
Linger 옵션이란 소켓종료 함수를 호출했을 때 아직 송신되지 않고 송신 버퍼에 남아있는 데이터를 어떻게 처리해야 하는지 OS에 알려주는 옵션이다.
만약 네트워크 오류로 인해 클라이언트 네트워크가 연결이 끊겼을 경우 서버에서 해당 클라이언트 소켓을 종료해 주는 과정에서 종료 절차에 문제가 생겨 서버쪽에 TIME_WAIT 또는 CLOSE_WAIT가 남게 되면 심각한 문제가 발생할 수 있다.
Linger 옵션을 사용하면 이러한 네트워크 문제를 남기지 않게 소켓을 종료할 수 있다.
l_onoff |
Linger 옵션 사용 여부 |
|
l_linger |
Linger Timeout 설정 |
CASE 1 : l_onoff = 1, l_linger = 0
소켓 종료 함수는 즉시 return하고, 출력 스트림 버퍼(Send buffer)에 남아있는 data는 버려진다.
비정상종료(Abortive Shutdown)
TIME_WAIT / CLOSE_WAIT 방지 가능
CASE 2 : l_onoff = 1, l_linger = 0이 아닌 정수값
정상적 종료가 완료될때 까지 소켓 종료 함수 return하지 않음. Graceful shutdown 진행
l_linger 에 명시된(Timeout) 시간이 지나도 정상적인 종료가 완료되지 않으면 비정상종료 진행. Send buffer에 남아있는 데이터는 모두 버려짐.
CASE 3 : l_onoff = 0
Default 옵션
소켓 종료함수 즉시 return 후 Graceful shutdown을 백그라운드에서 진행한다.
종료작업이 언제 완료되는지 확인할 수 없음.
<CASE 1 구현 코드>
'Network Programming' 카테고리의 다른 글
DNS 기본 동작 설명 (0) | 2019.10.29 |
---|---|
[TCP/IP]소켓의 다양한 옵션 (0) | 2016.06.26 |
[TCP/IP]DNS (0) | 2016.06.26 |
[TCP/IP]스트림 half-close (0) | 2016.06.26 |
[TCP/IP]UDP (0) | 2016.06.26 |