محدّث قبل شهر واحد
어떤 서버에서나 ss -tan을 실행하면 대부분의 사람들이 그냥 지나치는 것이 보입니다: 상태 열. ESTABLISHED. TIME_WAIT. CLOSE_WAIT. SYN_SENT.
이것들은 단순한 레이블이 아닙니다. 시간 속에 얼어붙은 대화입니다. 각 상태는 누가 마지막으로 말했는지, 그리고 무엇을 기다리고 있는지를 알려줍니다.
연결 상태란 무엇인가
TCP 연결은 두 기계 사이의 대화입니다. 모든 대화처럼 시작("안녕하세요"), 중간(실제 통신), 끝("안녕히 계세요")이 있습니다. 연결 상태는 그 대화에서 지금 어디쯤 있는지를 나타냅니다.
UDP는 상태가 없습니다. UDP에는 대화 자체가 없기 때문입니다. 패킷을 허공에 던지고 도착하기를 바랄 뿐입니다. 핸드셰이크도, 확인도, 추적할 상태도 없습니다.
하지만 TCP는 전달을 보장합니다. 그러기 위해 양쪽이 매 순간 무슨 일이 일어나고 있는지 합의해야 합니다. 연결 상태는 그 합의를 눈에 보이게 만든 것입니다.
상태들
LISTEN — 귀를 기울이며 기다리는 서버. "누군가 연결하고 싶으면 언제든지요." 포트 443의 모든 웹 서버, 포트 22의 모든 SSH 데몬이 여기서 시작합니다.
SYN_SENT — 클라이언트가 문을 두드렸습니다. 이제 밖에서 기다리는 중입니다. 너무 오래 기다린다면 뭔가 잘못된 것입니다. 서버가 다운되었거나, 방화벽이 패킷을 삼켜버렸거나, 주소 자체가 존재하지 않는 것입니다.
SYN_RECEIVED — 서버가 노크 소리를 듣고 "거기 누구세요?"라고 답했습니다. 이제 클라이언트가 핸드셰이크를 완료하기를 기다리고 있습니다. 여기에 수천 개의 연결이 멈춰 있다면 수천 개의 핸드셰이크가 공중에 매달려 있다는 뜻입니다—흔히 SYN 플러드 공격이 원인입니다. 위조된 연결 요청이 쏟아지면서 서버는 끝내 완료되지 않을 핸드셰이크를 기다리며 손을 내밀고 있는 셈입니다.
ESTABLISHED — 대화가 진행 중입니다. 데이터가 양방향으로 흐릅니다. 바로 이 상태가 정상이고, 우리가 보고 싶은 상태입니다.
FIN_WAIT_1 — 한쪽이 "이제 그만 이야기할게요"라고 했지만 아직 확인을 받지 못했습니다.
FIN_WAIT_2 — 작별 인사는 확인되었지만, 상대방이 아직 자신의 작별 인사를 하지 않았습니다.
CLOSE_WAIT — 원격 측이 전화를 끊었습니다. 우리 쪽도 이를 확인했습니다. 그런데 애플리케이션이 아직 연결을 닫지 않았습니다. 유령 같은 상태입니다—상대방은 이미 가버렸는데, 애플리케이션은 돌아오지 않을 누군가를 기다리며 여전히 문을 붙잡고 있습니다. 이런 연결이 대량으로 쌓인다면 애플리케이션에 버그가 있는 것입니다.
TIME_WAIT — 연결은 끝났지만, 오래된 대화에서 남은 패킷들이 여전히 인터넷을 떠돌고 있을 수 있습니다. TIME_WAIT는 같은 포트 번호를 재사용하는 새 연결이 그 패킷에 혼동되지 않도록 막아줍니다. Linux에서는 60초 동안 유지됩니다.1
CLOSED — 사라졌습니다. 시스템이 보기에 그 대화는 없었던 것입니다.
확인 방법
Linux에서:
macOS에서:
상태별로 연결 수를 세려면:
이 명령으로 서버의 대화 현황을 한눈에 파악할 수 있습니다.
패턴이 의미하는 것
SYN_SENT가 수백 개 — 아웃바운드 연결이 목적지에 도달하지 못하고 있습니다. 방화벽 문제? 라우팅 문제? 반대편 서버가 죽어 있는 건 아닐까요?
CLOSE_WAIT가 수백 개 — 애플리케이션이 연결을 제대로 정리하지 않고 있습니다. 원격 측은 연결을 닫았는데, 애플리케이션이 알아채지 못했거나 응답하지 않은 것입니다. 코드를 수정하세요.
TIME_WAIT가 수천 개 — 반드시 문제는 아닙니다. 트래픽이 많은 서버는 자연스럽게 이것들을 쌓아갑니다. 하지만 임시 포트(Linux 기본값은 32768–60999 범위에서 약 28,000개)2가 고갈되기 시작하면 포트 재사용을 활성화하거나 범위를 확장해야 합니다.
데이터 흐름 없이 멈춰 있는 ESTABLISHED 연결 — 대화가 멈췄지만 어느 쪽도 인정하지 않는 상황입니다. TCP keepalive를 활성화해서 이 좀비 연결들을 감지하고 정리하세요.
SYN_RECEIVED 적체 — 공격을 받고 있거나(SYN 플러드), SYN 큐를 압도할 만큼 트래픽이 급증하고 있는 것입니다. 백로그를 늘리세요. SYN 쿠키를 활성화하세요.
UDP의 침묵
UDP 소켓은 ss 출력에 나타나지만, 상태가 없습니다. ESTABLISHED도, TIME_WAIT도, 추적할 대화도 없습니다. 소켓이 열려 있거나, 그렇지 않거나. 패킷이 도착하거나, 그렇지 않거나.
이것이 UDP를 빠르게 만드는 이유입니다—핸드셰이크 오버헤드도 없고, 유지할 상태도 없습니다. 동시에 UDP를 맹목적으로 만들기도 합니다. 패킷이 사라져도 UDP는 모르고 신경도 쓰지 않습니다. UDP를 사용하는 애플리케이션(DNS, 비디오 스트리밍, 게임)은 신뢰성이 필요한 경우 직접 처리해야 합니다.
왜 이게 중요한가
연결 상태는 의도를 드러내기 때문에 진단에 있어 더없이 값진 정보입니다. SYN_SENT는 "연결을 시도하고 있다"는 뜻입니다. CLOSE_WAIT는 "전화를 끊는 걸 잊어버렸다"는 뜻입니다. TIME_WAIT는 "방금 막 끝났다"는 뜻입니다.
무언가 문제가 생겼을 때, 상태를 보면 대화의 어느 지점에서 고장이 났는지 알 수 있습니다. 그것만으로도 방화벽인지, 애플리케이션 버그인지, 네트워크 문제인지, 리소스 고갈인지를 추측 없이 파악하기에 충분합니다.
다음에 서비스가 멈추면, 상태를 확인하세요. 기계들이 무엇이 잘못되었는지 알려주려 하고 있습니다.
TCP 연결 상태에 관해 자주 묻는 질문
TIME_WAIT 연결이 왜 이렇게 많은가요?
TIME_WAIT 연결은 정상입니다. 같은 포트 번호를 재사용하는 새 연결이 오래된 패킷의 방해를 받지 않도록 하는 안전장치입니다. 트래픽이 많은 서버는 수천 개를 쌓아갑니다. 임시 포트가 고갈될 때만 문제가 되는데, 그 경우 새 연결 수립에 실패하는 형태로 나타납니다.
계속 쌓이는 CLOSE_WAIT 연결을 어떻게 수정하나요?
CLOSE_WAIT는 애플리케이션이 원격 측이 연결을 닫았다는 알림을 받고도 자신의 연결을 닫지 않았다는 의미입니다. 이것은 항상 애플리케이션 버그입니다. 해법은 코드에 있습니다: 원격 측이 연결을 끊었음을 감지했을 때 소켓을 올바르게 닫도록 하세요.
FIN_WAIT와 TIME_WAIT의 차이는 무엇인가요?
FIN_WAIT 상태는 연결이 현재 닫히고 있는 중임을 뜻합니다—한쪽이 작별 인사를 했고, 확인이나 상대방의 작별 인사를 기다리고 있는 상태입니다. TIME_WAIT는 연결이 완전히 종료된 뒤에 발생하며, 포트 번호가 재사용되기 전에 안전 버퍼를 제공합니다.
TIME_WAIT 지속 시간을 줄여서 포트를 더 빨리 해제할 수 있나요?
Linux에서 60초라는 TIME_WAIT 지속 시간은 하드코딩되어 있어 직접 변경할 수 없습니다. 더 나은 해결책으로는 tcp_tw_reuse 활성화, 연결 풀링, 또는 net.ipv4.ip_local_port_range로 임시 포트 범위 확장 등이 있습니다.
출처
هل كانت هذه الصفحة مفيدة؟