TCP의 TIME_WAIT상태 (SO_REUSEADDR)

http://wwwi.tistory.com/tag/winsock

TCP의 서버 프로그램을 종료한 바로뒤 다시 서버를 기동하면
bind에서 에러(Address aleady in use)로 끝날 때가 있다.

TCP의 서버 프로그램을 종료하고 다시 기동시켰는데 왜 bind가 되지 않을까?
라고 생각하며 시간이 지난후 다시 기동시키면 문제업이 bind가 된다.

이 문제는 TCP 자체 사양에 의하여 일어나는 문제이다.
구체적으로는 TCP의 TIME_WAIT상태가 bind를 fail시킨다.

서버는 TCP 세션을 받은 상태에서 close하면 TIME_WAIT상태가 된다.
이때 주의해야되는 것은 TCP 서버의 TIME_WAIT가 발생하는 경우와
발생하지 않는 경우가 있다는 것이다.

TCP서버 쪽에서 close를 먼저 실행하면 TIME_WAIT상태가 발생하지만
TCP클리아언트 쪽에서는 먼저 close를 실행해도 서버 쪽에
TIME_WAIT가 발생하지 않는다.
더 구체적으로 쓴다면 클라이언트가 먼저 FIN을 송신하면 TCP서버 쪽에는
TIME_WAIT상태에 빠지지않는다.

TIME_WAIT상태는 "netstat -na" 명령을 도스창에서 쳐보면 확인할 수 있다.
TCP세션이 확립된 경우 "netstat -na"를 쳐보면 ESTABLISHED라고 표시되어진다.
TCP세션이 종료된 후 "netstat -na" 명령을 쳐보면 TIME_WAIT생태를 볼수있다.

TIME_WAIT상태는 같은 포트를 다른 프로세스가 이용하는 것을 막기 위해
TCP 규격으로 규정되어져있다.
TIME_WAIT상태의 포트와 동일한 포트를 bind하려하면 bind는 실패한다.
단 끝나버린 프로세스가 쓰고 있는 포트 번호를 바로 쓸 수 없으면 곤란함으로
TIMW_WAIT상태로 남아있는 TCP세션이 있더라도 bind할 수 있는 방법이 있다.
그 방법이 SO_REUSEADDR을 유효하게 하는 것이다.

SO_REUSEADDR를 유효하게 하는 방법은 setsockopt함수를 이용하여
소켓에 옵션을 설정할 수 있다.


#include

#include


int main()

{

WSADATA wsaData;

SOCKET sockSvr;

SOCKET sockSS;

int nlen;

struct sockaddr_in addrSockSvr;

struct sockaddr_in addrSockclt;

BOOL bValid = 1;


// 윈속 초기화

WSAStartup(MAKEWORD(2, 0), &wsaData);


// 소켓 만들기

sockSvr = socket(AF_INET, SOCK_STREAM, 0);


// 소켓 설정

addrSockSvr.sin_family = AF_INET;

addrSockSvr.sin_port = htons(333);

addrSockSvr.sin_addr.S_un.S_addr = INADDR_ANY;


// 소켓 옵션 설정

setsockopt(sockSvr, // SOCKET

SOL_SOCKET, // level

SO_REUSEADDR, // Option

(const char *)&bValid, // Option Value

sizeof(bValid)); // Option length


bind(sockSvr, (struct sockaddr *)&addrSockSvr, sizeof(addrSockSvr));


// TCP클라이언트로 부터 접속 요구를 대기

listen(sockSvr, 5);


while (1) {


// TCP클라이언트로 부터 접속 요구 받기\tab

nlen = sizeof(addrSockclt);

sockSS = accept(sockSvr, (struct sockaddr *)&addrSockclt, &nlen);


// 문자송신

printf("%s 로부터 접속 (포트번호:%d)\n",

inet_ntoa(addrSockclt.sin_addr), // IP어드레스

ntohs(addrSockclt.sin_port)); // 포트번호


send(sockSS, "안녕", 5, 0);


closesocket(sockSS);

}


// 윈속 종료

WSACleanup();


return 0;


}


0 コメント:

コメントを投稿