TCP/IP Socket option "KEEP_ALIVE"
TCP/IP 의 SOCKET 옵션인,
SO_KEEP_ALIVE 에 대해서 알아보자.
TCP/IP 프로토콜은 메세지가 계속 오고 가는 중에는 ACK 를 TCP 헤더에 싣어서 보내기 때문에,
TCP 슬라이딩 윈도우에서 슬라이딩 해가면서, 그 연결 상태를 확인 할 수 있다.
그런데, 한동안의 휴지기를 가지고 대기하고 있는 socket 에서는 heartbeat 를 전송하지 않기 때문에, 이 소켓이 연결이 되었는지 안되어 있는지 확인할 방법이 없다.
따라서, 어플리케이션 레벨에서 일반적으로, keep alive 메커니즘을 구현하는 것이 일반적이다.
그런데, TCP 프로토콜 레벨에서, 이 연결 상태를 확인 할 수 있는 소켓 옵션이 있어서, 여기에
이렇게 정리하게 되었다.
그런데, 이 keep alive 메커니즘이란게, 일반적인 개념의 네트워크 서버혹은 클라이언트 작성을 하는데는, 매우 유용하지 않다.-_-;;
원래 TCP 에 도입된 keep alive 메커니즘 자체가, FIN 없이 연결을 끊는 장시간 지속 서버 데몬에서, SOCKET 들을 잘라내기 위해서 구현된 것이기 때문에,
그 디폴트 시간 간격이란게 무지하게 길다. "Effective TCP/IP" 에 따르면, 4.4 BSD 구현에서 이 디폴트 값이 장장 2시간 11분 15초로 잡혀 있다.
쉽게 말해서, 휴지기에 들어간 소켓 A가 내 컴퓨터에 있고, 이에 대응하는 소켓 B가 옆 컴퓨터에 있을때, 내 컴퓨터의 랜선을 뽑아도, keep alive 메커니즘이 2시간 여가 흐른 후에나 Keep alive 전송해서 연결을 확인 한다는 이야기이다.
물론 default 는 단지 default 우리는 이 값을 바꿀 수 있다.
setsockopt(SOCKET socket, int level, int optname, const char * optval, int optlen);
이란 socket api 를 이용해서, Windows / Unix 계열 양쪽에서
다음과 같이
struct tcp_keepalive keepalive_t;
keepalive_t.l_onoff = xx(0이 아닌 값);
keepalive_t.keepalivetiime = 100;//밀리세컨드(default 는 75초 in BSD4.4)
keepalive_t.keepaliveinterval = 2000;//밀리케선드 (default 는 두시간정도 --;;)
int bRet = setsockopt(socket, SOL_SOCKET,SO_KEEPALIVE,&tcp_keepalive,
sizeof (keep_alive_t));
설정 해줄 수 있다.
keepalivetime 은 휴지기가 시작된 이후 처음으로 keepalive 를 전송할 간격이고,
keepaliveinterval 은 keepalive 에 대한 응답이 안올 때, 9번의 재전송을 하는데 이때 걸리는
시간을 의미한다.
그런데, 위 방법은 일반적으로 권장되지 않는 방법이다. 그 이유는 KEEP ALIVE 를 해당 socket 에
대해서만 켜는 것이 아니라, 시스템의 모든 소켓에 대해서 설정하기 때문이다.--;;
또한 keepalive 는 단순히 다운되어 있는 연결을 찾기만 하는 것이 아니라, 그런 연결을 찾아서 버린다는 데 문제점이 있다.
물론, traffic 많지 않은 모바일 게임 서버들에서 어플리케이션을 정상 종료하지 않고 단순히 핸드폰 뚜껑을 닫아버리는 많은 일반인들의 행동에 의한, 다운된 연결을 찾는 등에는 유용히 쓰일 수 있다.
이때에도 socket 마다 이를 설정해줄 수 있어야 할 것이다.
다행이도, POSIX 와 Winsock2 에서는 각각 이에 해당하는 옵션들을 제공하고 있는데, 여기 이르면, 양쪽의 구현 완성도와 쓰는 방법이 차이가 난다.
여기서는 일단 winsock2 에서 소켓 각각에 tcp_keepalive 를 설정하는 예제를 살펴보자.
TestClient.cpp 이다.
int _tmain(int argc, char ** argv)
{
struct sockaddr_in peer;
struct tcp_keepalive keep_alive;
WSADATA wsaData;
WSAStartup(MAKEWORD(2,2),&wsaData);
SOCKET s;
int rc;
char buf[1];
peer.sin_family = AF_INET;
peer.sin_port = htons (12500);
peer.sin_addr.s_addr = inet_addr("10.20.8.49");
keep_alive.onoff = 1;
keep_alive.keepaliveinterval = 1000L;
keep_alive.keepalivetime = 2000L;
s = socket(AF_INET, SOCK_STREAM, 0);
if(s < 0)
{
perror("socket call failed");
exit(1);
}
rc = connect(s,(struct sockaddr *)&peer,sizeof(peer));
if(rc)
{
perror("connect call failed");
exit(1);
}
struct tcp_keepalive out_keep_alive;
DWORD outByte;
WSAIoctl(s,SIO_KEEPALIVE_VALS,&keep_alive,sizeof(keep_alive),&out_keep_alive,sizeof(out_keep_alive),&outByte,NULL,NULL);
char buff[1024] = {'x', };
for(int i = 0; i < 10; i++)
{

rc = send(s,buff,100,0);
if( rc <= 0)
{
perror("send call failed.");
exit(1);
}
memset(buff, 0, sizeof(buff));
rc = recv(s,buff, sizeof(buff), 0);
if(rc <= 0)
{
perror("receive failed.");
exit(1);
}
else
{
printf("%sn",buff);
}
::Sleep(1000);
}
rc = recv(s,buff, sizeof(buff), 0);
if(rc <= 0)
{
perror("receive failed.");
exit(1);
}
else
{
printf("---%sn",buff);
}

exit (0);
}
극악 단순한, send / recv pair 열개와 마지막에 한번의 recv 를 기다리고 있는 형태이다.
by 悠悠自適 | 2007/02/13 17:58 | 情報通信 | 트랙백 | 덧글(1)
트랙백 주소 : http://uuzazuk9.egloos.com/tb/906643
☞ 내 이글루에 이 글과 관련된 글 쓰기 (트랙백 보내기) [도움말]
Commented by 박은선 at 2008/07/24 14:03
좋은정보 얻어갑니다..
감사해요.

:         :

:

비공개 덧글



<< 이전 페이지 다음 페이지 >>