|
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 KCry at 11/19 <a href="http://ww.. by asas at 10/20 블로그구경 잘하고 갑니.. by bangtae at 11/18 퍼갑니다. http://blog... by 에르데 at 10/24 유익한 정보였습니다. .. by koohyun at 08/19 좋은정보 얻어갑니다.. .. by 박은선 at 07/24 별말씀을.... 저도 긁.. by 悠悠自適 at 06/24 좋은자료들을 보여주셔서.. by 미동 at 06/19 프로그래밍에 관해 좋은.. by 알렉수 at 01/30 눈에 띄는 링크들이 있어.. by MK at 10/13 최근 등록된 트랙백
라이프로그
포토로그
메모장
이전블로그
이글루링크
이글루 파인더
태그
|