Network/Network TCP 소켓 옵션 SO_REUSEADDR
  • 728x90
    반응형

     

     

     

    TCP/IP 소켓 옵션

    feat. SO_REUSEADDR


     

    이슈

    📌 Server를 생성하고 Client 가 접속한 후 Client가 접속된 상태에서 Server를 종료시키고자 한다. Server socket과 accept로 생성된 socket을 close() 함수로 종료한 후 Task를 다시실행하였더니 bind error 가 발생하였다. 즉 Socket 이 제대로 닫히지 않은 것 이다.

     

     

    ✨ 4 wat handshake

     

    🎯 4 way handshake 설명

    최초에는 서로 통신 상태이기 때문에 양쪽이 ESTABLISHED 상태이다.

    1. 통신을 종료하고자 하는 Client가 서버에게 FIN 패킷을 보내고 자신은 FIN_WAIT_1 상태로 대기한다.
    2. FIN 패킷을 받은 서버는 해당 포트를 CLOSE_WAIT으로 바꾸고 잘 받았다는 ACK 를 Client에게 전하고 ACK를 받은 Client는 상태를 FIN_WAIT_2로 변경한다. 그와 동시에 Server에서는 해당 포트에 연결되어 있는 Application에게 Close()를 요청한다.
    3. Close() 요청을 받은 Application은 종료 프로세스를 진행시켜 최종적으로 close()가 되고 server는 FIN 패킷을 Client에게 전송 후 자신은 LAST_ACK 로 상태를 바꾼다.
    4. FIN_WAIT_2 에서 Server가 연결을 종료했다는 신호를 기다리다가 FIN 을 받으면 받았다는 ACK를 Server에 전송하고 자신은 TIME_WAIT 으로 상태를 바꾼다. (TIME_WAIT 에서 일정 시간이 지나면 CLOSED 되게 된다.)

     

    최종 ACK를 받은 서버는 자신의 포트도 CLOSED로 닫게 된다. 현재 포트의 상태는 netstat명령어를 통해 확인할 수 있다. 위와 같이 종료과정을 살펴보면 close()호출후에도 대기 시간이 발생한다. 이와 같은 소켓 재사용 문제를 해결하기 위해 소켓옵션을 사용한다.

     

    SO_REUSEADDR옵션을 설정하면 커널이 소켓을 사용하는 중에도 계속해서 사용할 수 있다. 이 옵션은 서버프로그램이 종료된 후에도 커널이 소켓의 포트를 아직 점유 중인 경우에 서버 프로그램을 다시 구동해야 할 때 매우 유용하다.

     

    예를 들어, 연결 종료 단계에서 설명한 바와 같이 연결 종료를 먼저 시작한 쪽은 상대방으로부터 FIN 패킷을 받고, FIN_ACK 패킷을 전송한 후 일정 시간 동안 소켓을 종료하지 않고 커널이 해당 소켓을 점유한다.

     

    즉, 먼저 종료를 시작하는 시스템은 응용 프로그램이 종료되더라도 소켓은 커널에서 일정 시간 동안 점유 중인 상태로 있게 되는 것이다. 이런 경우에 응용 프로그램을 재실행하면 bind함수를 호출할 때 아직 점유 중인 포트를 연결하려는 시도 때문에 오류가 발생한다. 일정 시간이 지나야 연결이 가능하다.

     

    게임 서버처럼 사용자가 많은 서버에서 버전 업그레이드 때문에 일정 시간 동안 서버를 중단했다가 다시 구동해야 하는 경우에도 이런 문제가 발생한다. 일정 시간 서비스를 할 수 없는 것이다. 이때 SO_REUSEADDR옵션을 설정하면 커널이 소켓의 포트를 점유 중인 상태에서도 서버 프로그램을 다시 구동할 수 있다.

     

    이 밖에도 두 개 이상의 IP 주소를 갖는 호스트에서 IP 주소별로 서버를 운용할 경우에도 이 옵션을 사용하면 사용 중인 포트에 대해서도 소켓을 성공적으로 주소에 연결(bind)할 수 있다. 멀티캐스팅 응용 프로그램이 동일한 포트를 사용할 때도 이 옵션을 활용한다.

     

     

    예제 코드

    함수 원형 (VxWorks)

     

    🔔 예)

    int optval = 1;

    setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &optval, sizeof(optval));

    bind(s, &sin, sizeof(sin));

     

     

     

     

     

    728x90
    반응형
상단으로