소켓 close를 할때 우리는 보통 아무 옵션을 주지 않고 그냥 closesocket() 함수를 부를 것이다.

이렇게 닫힌 소켓은 TIME_WAIT상태로 대략 4분여간 뻐팅기게(?) 되는데 만약, 내가 만든 프로그램이 소켓을 대량으로 접속했다 끊었다를 반복하는 놈이라면?

만약 이 4분동안 시스템의 최대 열 수 있는 소켓 개수를 넘어버린다면?

이때부텀 이제 이 프로그램은 정상적인 동작을 할 수 없을것이다.


이 때 쓰는 것이 setsockopt함수인 것이다. 이 함수에 대한 자세한 내용은 MSDN이나 MAN 페이지를 참고하면 되고,,

아무튼, close하기 전에 특별한 일이 없다면 SO_LINGER옵션을 끄자.

이 옵션은 아직 전송되지 않은 데이터가 있을 경우 소켓을 종료(close)할때 대기하는 옵션이다. 이옵션땜시 중간에 close를 하면 서로 소켓끼리 sync를 보내다가 버퍼에 내용물이 남게되고 이로인해 결국 TIME_WAIT상태가 되어버린다..


이 SO_LINGER옵션을 이제 끄고, closesocket을 해보자.. 그러면 TIME_WAIT이고 머고 자시고 할 것 없이 깨끗하게 소켓이 죽어 없어지는 것을 볼 수 있을 것이다.


만약 자신이 어떤 네트워크 서버에 대한 스트레스성 테스트 클라이언트를 제작하고 있다면 반드시 이 옵션을 줘서 close를 해야 할 것이다.
===========================
LINGER val;
  int len = sizeof(val);

  val.l_onoff = 0; //off
  val.l_linger = 100;

  re = getsockopt(sock,SOL_SOCKET,SO_LINGER,(char*)&val,&len); //l_onoff = 0 l_linger = 100


http://ojktx.egloos.com/1112911

=========================================
NMBCLUSTERS 의 수만 늘리면 말 그대로 mbuf cluasters의 값만 늘어날
뿐입니다. socket의 갯수를 늘려주려면 maxfiles 의 값을 늘려줘야 하는데
커널설정시 MAXUSERS를 늘려주면 이와 관계된 값들이 따라서 늘어나니
MAXUSERS를 늘리고 컴파일을 하시는게 좋겠군요.

MAXUSERS를 224로 늘리면 maxfiles의 값이 4096으로 늘어납니다.
물론 이에 따라 NMBCLUSTERS의 값도 따라서 늘어납니다.

아마도 공식은 maxfiles = ( 16 * MAXUSERS ) + 512 일 겁니다.
===========================================


아래는 SO_LINGER를 이용하여 TIME_WAIT 상태가 안생기도록 설정한 프로그램입니다.
클라이언트 프로그램입니다. (혹시 서버쪽 소켓에 설정하셨는지?)
Unix Network Programming 책의 423 페이지에 나와있는 소스를 Linux에 맞게 약간 변형하였습니다.
테스트 해보니 잘 되는군요.

코드:
#include
#include
#include
#include
#include

int main(int argc, char **argv)
{
       int sockfd;
       struct linger ling;
       struct sockaddr_in servaddr;

       if (argc != 2)
       {
               fprintf(stderr, "Error\n");
               exit(1);
       }

       sockfd = socket(AF_INET, SOCK_STREAM, 0);

       bzero(&servaddr, sizeof(servaddr));
       servaddr.sin_family = AF_INET;
       servaddr.sin_port = htons(21345);
       inet_aton(argv[1], &servaddr.sin_addr);

       connect(sockfd, (struct sockaddr *)&servaddr, sizeof(servaddr));
       ling.l_onoff = 1;
       ling.l_linger = 0;
       setsockopt(sockfd, SOL_SOCKET, SO_LINGER, &ling, sizeof(ling));


       close(sockfd);
       return 0;
}

=============================================

  • SO_LINGER

SO_LINGER 옵션이 있습니다. 이것은 TCP 에서 적용되는 것인데 close함수의 행동을 지정하는 옵션입니다. close() 하면 recv Buffer 나 send Buffer 에 보내거나 받을 데이터가 있다면 전부 처리 후 close() 를 합니다. 그 방법을 바꾸는 것입니다. 먼저 전달되는 구조체에 대해서 알아 보도록 합시다.

struct linger { int l_onoff; int l_linger; }; setsockopt( sock, SOL_SOCKET, SO_LINGER, &linger 구조체 주소, sizeof( linger ) ); 

이런 식으로 호출하면 되겠죠. 그리고 세부적인 동작 설정은 linger구조체의 변수 설정에 있습니다.

  1. l_onoff가 0이면 기본적인 TCP동작이 적용됩니다.
  2. l_onoff가 0이 아니고(주로 1을 넣습니다.) l_linger가 0이면 연결이 닫힐 때 버퍼의 내용을 버리고 연결을 끊어 버립니다.
  3. l_onoff가 0이 아니고 l_linger도 0이 아니면 소켓이 닫힐 때 블럭 당한다고 합니다.

이 소켓옵션을 쓸 땐 2번을 주로 씁니다. 쓰는 이유는 만약 서버가 종료되고 다시 시작 할 때 입니다. 연결이 끊어지고 남은 데이터를 전송합니다. 그때 남은 데이터를 보낸다면 클라이언트에게 ack 메시지(받았다는 확인 메시지)를 받아야 완전한 종료가 이루어집니다. 그 메시지를 기다리는 시간이 있습니다. 만약 그것을 다 받지 못했다면 다시 보내야 하지요. 그런 상황에서 다시 서버를 시작하려고 하면 이미 사용 중인 포트라는 에러를 내게 됩니다. 그래서 이런 옵션을 사용하는 것입니다. 그런데 그것은 바람직한 해결 방법이 아니라고 합니다. 그래서 이런 옵션은 추천되고 있지 않습니다. 이에 대한 해결책은 따로 있습니다. 그것이 다음에 설명할 포트 재사용 옵션입니다.

  • SO_REUSEADDR

이 옵션을 선택하여 주면 위의 예에서 말한 서버 재 시작 시 다시 시작할 수 있습니다. 간단히 사용법을 알아보도록 하지요.

int nResue = 1; setsockopt( ListenSocket, SOL_SOCKET, SO_REUSEADDR, &nReuse, sizeof( nReuse ) ); 

이것도 호출 순서가 있는데 bind() 하기 전에 이 옵션을 설정해 놓아야 합니다. 이렇게 하면 소켓의 포트를 재 사용할 수 있습니다.

+ Recent posts