안녕하세요 쿤드입니다. 🍀

RFC 793에 나와있는 상태도를 바탕으로 하나씩 따라가면서 확인하면 보입니다.

python2로 구현된 간단한 tcp client/server 코드와 함께 확인해보겠습니다.

 

* TCB: Transmission Control Block


 

ESTABLISHED까지는 1가지 경우인데
종료할때는 2가지 case로 나뉩니다. RFC793 Link

  • Normal Close(일반 종료): FIN-WAIT-1 ➡️ FIN-WAIT-2 ➡️ TIME-WAIT
  • Simultaneous close(동시 종료): FIN-WAIT-1 ➡️ CLOSING ➡️ TIME-WAIT
일반 종료 
- 어느 한쪽에서 종료하기 위해 FIN을 날린 경우
동시 종료
- Clinet, Server 양쪽에서 동시에 FIN을 날린 경우

Python Code로 테스트 (Code link)

Server Client

 

Server쪽에서 일반 종료인 경우 netstat으로 확인해서 보았습니다.

 

Server에서 socket() 열고, bind()하고 listen() 함수 호출해서 Listening을 하면

  tcp        0      0 0.0.0.0:9999            0.0.0.0:*               LISTEN      8268/python          off (0.00/0/0)

 

Client에서

connect() 호출하면
  tcp        0      0 SERVER_IP:9999      CLIENT_IP:36391     ESTABLISHED 8268/python          off (0.00/0/0)

 

send(), recv() 호출 이후 종료
  tcp        0      1 SERVER_IP:9999      CLIENT_IP:36391     FIN_WAIT1   -                    on (0.17/0/0)
  tcp        0      0 SERVER_IP:9999      CLIENT_IP:36391     FIN_WAIT2   -                    timewait (8.99/0/0)
  tcp        0      0 SERVER_IP:9999      CLIENT_IP:36391     FIN_WAIT2   -                    timewait (7.97/0/0)
  tcp        0      0 SERVER_IP:9999      CLIENT_IP:36391     TIME_WAIT   -                    timewait (59.90/0/0)
  tcp        0      0 SERVER_IP:9999      CLIENT_IP:36391     TIME_WAIT   -                    timewait (58.88/0/0)
  tcp        0      0 SERVER_IP:9999      CLIENT_IP:36391     TIME_WAIT   -                    timewait (57.86/0/0)


CLOSE_WAIT 상태는 상태도만 봤을때 이해가 잘 안가서 Server/Receiver 쪽에서 억지로 재현해보았습니다.

  • Client/Sender에서 ESTABLISHED 이후에 data를 보냄
  • Server/Receiver에서 'ACK!' 라고 응답 (60sec 대기)
  • Client/Sender에서 data를 보냄
  • Server/Receiver는 여저히 sleep으로 대기상태
# 서버쪽 응답하는 thread 코드
def handle_client_connection(client_socket):
    while True:
        request = client_socket.recv(1024)
        print 'Received {}'.format(request)
        print 'send ack & sleep'
        try:
            client_socket.send('ACK!')
            time.sleep(60)  # <--- 여기서 막음 
        except socket.error as error:
            break
    client_socket.close()
  • 이때 Client/Sender는 응답을 못 받아서 대기중인 상태인데 강제로 종료 (Ctrl + C, KeyboardInterrupt)

 

이렇게 하는 경우 또 다른 client에서 붙고 마찬가지로 멈춰버린 상태에서 client process 강제 종료


tcp        0      0 0.0.0.0:9999            0.0.0.0:*               LISTEN      17043/python         off (0.00/0/0)
tcp       54      0 SERVER_IP:9999      CLIENT1_IP:39099     CLOSE_WAIT  17043/python         off (0.00/0/0)
tcp       54      0 SERVER_IP:9999      CLIENT2_IP:47657      CLOSE_WAIT  17043/python         off (0.00/0/0)

 

이 예시에서는 sleep 60으로 60s 지나면 해소가 되는데

Deadlock이라도 걸려서 저 상태가 계속해서 지속되면 결국엔 CLOSE_WAIT이 쌓여서 사용 가능한 PORT를 전부 사용하게 될테고

서버가 행업이 됩니다.

저 상태에서 Server side가 close를 호출하지 못하면 CLOSE_WAIT은 자동으로 사라지지도 않습니다.

 

이것이 실제 production 환경에서 발생한다면

dead lock 걸린 부분을 찾아서 소스 코드를 수정하기 전까지는 

시스템 엔지니어는 주기적으로 CLOSE_WAIT 개수를 체크해서 서버 프로세스를 재시작해주면서 버티는 방법밖에는 없어보입니다.

반응형

'기타' 카테고리의 다른 글

Python3를 이용한 Tistory Authentication Code 방식 인증 (2020.09.01 변경사항 반영)  (2022) 2020.09.25
테넌트  (656) 2020.07.23
TCP 통계수치 변화 (장애 상황)  (1029) 2020.07.15
TCP 통계  (145) 2020.07.14
Kerberos Setup (KR)  (964) 2019.08.22

+ Recent posts