HTTP통신에서 CLOSE_WAIT 상태로 Hang이 걸렸을 때 대처

.NET에서는 HttpClient를 통해 HTTP 통신을 많이 합니다. 물론 RestSharp도 많이 쓰지만요. 포트관리가 디테일하게 어떻게 되는지 모르겠어서 쓰지 않고 있습니다.

HTTP 통신 프로세스 중 TCP를 이용한 프로세스를 보면 소켓을 닫을 때 CLOSE_WAIT 상태로 Hang이 걸려서 더 이상 프로세스가 진행이 되지 않는 경우를 종종 볼 수 있습니다. 저도 현재 가끔 겪고 있습니다. 저의 경우는 클라이언트 콘솔 프로젝트에서 가끔 겪고 있어서, 이 경우 그냥 모니터링하고 있다가 netstat을 CMD에 쳐보고 CLOSE_WAIT이 다수의 포트에 걸려 있을 경우 프로그램을 종료하고 2분 정도 후에 프로그램을 다시 시작하곤 합니다.

하지만 이런 행위의 작업은 추후 docker 이미지로 빌드해서 컨테이너로 동작할 때는 자동화 되지 않은 방법이라 사용할 수 없기에 이 경우 어떻게 reset 해야하나 고민입니다. 반복적으로 CLOSE_WAIT 상태에 있다면 코드에 어떤 문제가 있어서 네트워크 통신이 중지되는 것 일텐데 발생할 때도 있고 발생하지 않을 때도 있습니다.

CLOSE_WAIT이 걸리지 않는 노하우나, CLOSE_WAIT을 감지했을 때 프로세스를 재시작하는 방법이 따로 있을까요?

추가적으로, 이것이 TCP일 때 생기는 문제점인데 곧 출시될 QUIC은 UDP 방식이니까 이런 현상이 없어질까요?

3개의 좋아요
  1. CLOSE_WAIT는 비정상 종료가 발생할 경우 생긴다고 합니다. (제가 아직 대량의 CLOSE_WAIT 경험이 없어서 정확한 답변은 저는 힘듭니다; )
    image
    위의 핸드쉐이킹에서 오렌지 박스의 처리가 되지 않을 경우에 해당됩니다.

  2. QUIC는 UDP 기반이므로 CLOSE_WAIT 또는 TIME_WAIT 상태가 없을 것이라는 것은 확실합니다.

3개의 좋아요

2번의 답변이 게비스콘처럼 시원합니다…ㅎㅎ 감사합니다!! 뭔가 QUIC에서의 상태코드로 CLOSE_WAIT과 유사한 것이 생기려나…궁금하지만 그것은 써봐야 알겠군요 ㅎㅎ

1번의 대량의 CLOSE_WAIT의 경우 뜰 때도 있고 안 뜰 때도 있어서 저도 발생시키기가 힘든 상황입니다 ㅠ

2개의 좋아요

저또한 QUIC의 스팩을 뚫고 있지는 않으므로 모르지만, 아마도… CLOSE_WAIT 문제는 왕왕 벌어질 수 있는, 어쩌면 TCP 설계상의 문제라고도 할 수 있는 문제로 보이니, QUIC의 연결상태에서는 개선되지 않았을까 하는 추측을 해봅니다.

어쨌든, TCP나 UDP나 물리적 단선은 감지하지 못해요. 결국에는 heartbeat에 의해 의도치 않은 끊어짐을 감지할텐데, 재밌는건 QUIC는 심지어 IP가 변경되어도 잘 연결을 유지한다 합니다. 연결을 식별하는게 IP가 아니라 연결 ID라고 해요. 해서 QUIC와 홀펀칭과 궁합이 잘 맞을것이라는 기대감이 있습니다.

3개의 좋아요

성태님의 아래 글과 댓글 링크를 보시면 도움이 되지 않을까 해서 링크를 걸어봅니다.

.NET Framework: 333. 코드로 재현하는 소켓 상태(FIN_WAIT1, FIN_WAIT2, TIME_WAIT, CLOSE_WAIT, LAST_WAIT) (sysnet.pe.kr)

2개의 좋아요

재미있는 문제군요. 혹시 HttpClient를 전역으로 정적 인스턴스를 만들어 쓰시나요? 아니면 사용할 때마다 생성해서 쓰시나요?

그리고, 원래 CLOSE_WAIT은 명백히 응용 프로그램의 버그 수준으로 봐야 합니다. 이것이 발생하는 것은, 위에서 @dimohy 님이 올려주신 4-way handshake 다이어그램에도 나오지만 상대측에서는 소켓 close를 했지만 우리 측에서는 명시적으로 소켓 close를 하지 않은 것입니다.

내용 정정: CLOSE_WAIT은 우리 측에서 close를 하지 않으면 (윈도우의 경우) 2분 정도 후에 제거됩니다. ^^;

4개의 좋아요

@kevin13 @dimohy 답글 감사드립니다!

Console App에서 Generic Host & IHttpClientFactory를 통해 사용 중입니다. 물론 HttpClient 통신할때마다 객체를 새로 만들어서 사용하지는 않고, Base Uri이 여러개 필요한 경우가 있어 최초 생성자에서 5개만 생성해서 각자 역할에 맞게 실행됩니다. 위의 설명에서 가끔씩 Hand이 걸린다는 것은 그 5개 중 HttpClient 1개를 사용했을 때 생기는 문제입니다. 매번 발생하지는 않습니다…

HTTP 통신에 관심이 많아서 검색하다보니 정성태님 블로그 글도 보게 되었니다. 질문 글에 2분 후에 다시 시작한다는 것도 정성태님 블로그 글에서 보고 그렇게 수행했던 것입니다 ㅎㅎ 유익한 정보 감사드립니다!!

정보 공유 목적으로, 제가 보고 .NET HTTP 이해에 대해 도움이 되었던 글을 링크하겠습니다.

https://www.sysnet.pe.kr/2/0/12450

https://www.sysnet.pe.kr/2/0/1334

3개의 좋아요