TIME-WAIT 상태란, 연결 종료 시 마지막 패킷 전송 실패를 대비하기 위한 상태이다. TCP 연결 종료과정은 four-way handshaking을 하게 된다. TCP 에서 연결을 종료를 그림으로 설명하면 다음과 같다.

사용자 삽입 이미지

A : B 야, 이제 전화 끊자
B : 어~ 잠시만 기달려주셈
B : 이제 됐다, 전화 끊으삼
A : 끊어야지
 

마지막에 A 가 끊어야지 하고, TIME-WAIT 에 들어갑니다.
이유인즉, TIME-WAIT 에 있는 동안 혹시나 마지막에 B 에게 보낸 ACK 메세지가
도착하지 않았을 경우, B 에서 A 에게 다시 ACK, FIN 메세지를 보내게 됩니다.
그러면 A 는 TIME-WAIT 에서 빠져나와서
B 에게 ACK 메세지를 보내고 다시 TIME-WAIT 에 들어가게 됩니다.

For-way handshaking 과정이 끝난 상태에서 A의 소켓이 바로 소멸되는 것이 아니라 TIME-WAIT상태로
들어가게 된다. 반면에 B는 바로 연결이 종료되고 소켓이 소멸된다. 즉, 먼저 연결 종료를 요청할 경우 TIME-WAIT상태를
거쳐야 하다는 뜻이다.

만약, A가 먼저 연결을 종료(Ctrl-C 키) 시켰다고 하자. 그럼 A가 B에게 FIN메시지를 시작으로 four-way
handshaking을 하게 된다. 이어서 다시 프로그램을 실행 시키면, bind()함수 호출에서 문제가 발생했다고 나올
것이다. 이 이유가 바로 TIME-WAIT 때문이다. A의 TIME-WAIT가 일정시간 계속 유지 되고 있기 때문에 즉, 아직
소켓이 소멸된 것이 아니기 때문에(해당 port가 사용 중이기 때문에) bind()함수에서 에러가 난 것이다.

그럼 TIME-WAIT는 왜 갖는 것일까? 바로 마지막 패킷이 제대로 전송이 되었는지를 확인하기 위해 필요한 것이다. 위
그림에서 B에서 A에게로 “종료 준비가 완료 됐다”는FIN메시지를 보내고 A는 “자기가 종료 했다”는 메시지를 B에게 보내게
된다. 하지만 “자기가 종료를 했다”는 메시지를 B가 받지 못했을 경우 B는 또 다시 “종료 준비가 완료 됐다”는 FIN메시지를
A에게 보내게 되는데, 만약 TIME-WAIT가 없어서, A가 바로 종료를 하게 되었다면, 원활한 종료가 이루어지지 않을
것이다.

그래서 TIME-WAIT가 있으면 B가 A에게 재전송한 FIN메시지를 받을 수 있게 되고 A는 다시 “자기가 종료를
했다”라는 메시지를 보내게 되어 정상적인 종료를 이끌게 된다. 만약에 또 다시 “자기가 종료를 했다”라는 메시지를 보내게 되면,
또 일정 시간의 TIME-WAIT 시간이 주어지게 된다. 그래서 TIME-WAIT시간은 더 길어질 수도 있다.

만약 TIME-WAIT 시간이 모두 지나도 B 로부터 응답이 오지않는다고 가정하면,
A 는 그제서야 IP 에 연결된 포트를 소켓으로부터 반환하게 됩니다.

혹시 모를 패킷 전송 실패에 대비하기 위해 TIME-WAIT 이 존재하는 것입니다.
사용자 삽입 이미지

하지만, 안정적인 동작을 위해 만든 TIME-WAIT 이 사용자들에겐 답답함을 줄 수 있다고 하는데요.

사용자 삽입 이미지

실제 TIME-WAIT 이 2~3분 정도 걸리는 시간이기 때문에,만약 서버가 재가동을 위해 TIME-WAIT 에 머물러
있는 경우 이 때의 사용자들은 답답해할 수 있다는 문제입니다. 그래서 TCP/IP 는 TIME-WAIT 에 빠져있는 소켓을 다시
사용할 수 있는 방법을 제공하고 있습니다.

serv_sock=socket(PF_INET, SOCK_STREAM, 0);  
optlen = sizeof(option);  
option = TRUE;   // #define TRUE 1  
setsockopt(serv_sock, SOL_SOCKET, SO_REUSEADDR, &option, sizeof(option));

위 코드를 사용하면, TIME-WAIT 상태의 소켓도 바로 바인딩하여 사용할 수 있습니다.

다음은 TIME-WAIT 소켓을 바로 재사용하는 예제입니다.

#include <stdio.h>  
#include <stdlib.h>  
#include <string.h>  
#include <unistd.h>  
#include <arpa/inet.h>  
#include <sys/types.h>  
#include <sys/socket.h>  
 
#define TRUE 1  
#define FALSE 0  
void error_handling(char *message);  
 
int main(int argc, char **argv)  
{  
    int serv_sock;  
    int clnt_sock;  
    char message[30];  
    int str_len;  
      
    int option;  
    socklen_t optlen;  
      
    struct sockaddr_in serv_addr;  
    struct sockaddr_in clnt_addr;  
    int clnt_addr_size;  
      
    if(argc!=2){  
        printf(“Usage : %s <port>\n”, argv[0]);  
        exit(1);  
    }  
      
    serv_sock=socket(PF_INET, SOCK_STREAM, 0);  
    if(serv_sock == -1)  
        error_handling(“socket() error”);  
      
    optlen = sizeof(option);  
    option = TRUE;  
      
    /* setsockopt(serv_sock, SOL_SOCKET, SO_REUSEADDR, &option, sizeof(option)); */ 
    memset(&serv_addr, 0, sizeof(serv_addr));  
    serv_addr.sin_family=AF_INET;  
    serv_addr.sin_addr.s_addr=htons(INADDR_ANY);  
    serv_addr.sin_port=htons(atoi(argv[1]));  
 
    if(bind(serv_sock, (struct sockaddr*) &serv_addr, sizeof(serv_addr)))  
        error_handling(“bind() error “);  
      
    if(listen(serv_sock, 5)==-1)  
        error_handling(“listen error”);  
    clnt_addr_size=sizeof(clnt_addr);      
    clnt_sock=accept(serv_sock, (struct sockaddr*)&clnt_addr,&clnt_addr_size);  
 
    /* 데이터 수신 및 전송 */ 
    while( (str_len=read(clnt_sock,message, sizeof(message))) != 0)  
    {  
        write(clnt_sock, message, str_len);  
        write(1, message, str_len);  
    }  
    close(clnt_sock);  
    return 0;  
}  
 
void error_handling(char *message)  
{  
    fputs(message, stderr);  
    fputc(‘\n’, stderr);  
    exit(1);  
}

여기서 중간에 주석처리한 부분을 해제하면 TIME-WAIT 소켓을 바로 재사용할 수 있게 됩니다. 주석을 해제하고
컴파일하여 프로그램을 실행한 뒤에. CTRL+C 로 강제 종료를 하고 다시 프로그램을 실행합니다. 아래 스샷은 TIME-WAIT
상태의 소켓을 재사용하려 할 때 오류가 발생하는 부분을 설명한 것입니다. (주석을 해제하지 않음)
사용자 삽입 이미지

[출처] TCP TIME-WAIT 상태 이해하기 |작성자 몽키몽키

———————————————————————————–
서버의 TIME_OUT 시간 변경
   – 솔라리스의 경우

ndd /dev/tcp tcp_time_wait_interval
ndd -set /dev/tcp tcp_time_wait_interval 20000

———————————————————————————–
리눅스의 경우 TIME_WAIT값을 /etc/sysctl.conf 에 저장해놓고
(net.ipv4.tcp_fin_timeout = 1200 (초) 형식이라고 하네요..)
리부팅을 하던가 아니면 /proc/sys/net/ipv4/tcp_*항목을 수정하고
네트웍을 다시 시작하면 된다고 그럽니다.
(echo 1200 > /proc/sys/net/ipv4/tcp_fin_timeout )
솔라리스의 경우는 ndd유틸리티를 쓰면 됩니다.
(ndd /dev/tcp \? 라고 타이핑하시면 설정할수 있는 값들이 나오는데
거기에서 ndd -set /dev/tcp tcp**** VALUE 하시면 되는것 같습니다.
맨페이지에 자세히 나와있습니다.)