1. Βιβλιοθήκη
  2. TCP와 UDP
  3. TCP 심층 분석

Ενημερώθηκε πριν από 1 μήνα

"연결이 피어에 의해 재설정되었습니다." 이 오류는 거의 아무것도 알려주지 않습니다. 뭔가 잘못되었고, 상대방이 연결을 끊었다는 것뿐입니다. 하지만 어떻게 끊었는지—정중하게, 아니면 갑작스럽게—에 따라 무슨 일이 일어났는지, 어떻게 고쳐야 하는지가 완전히 달라집니다.

TCP에는 연결을 종료하는 두 가지 방법이 있습니다. FIN은 "말하기는 끝났지만, 아직 듣고 있어요"라고 말합니다. RST는 "이 대화는 없었던 일입니다"라고 말합니다.

FIN: 정중한 작별 인사

소켓을 정상적으로 닫을 때, TCP는 4방향 핸드셰이크를 시작합니다. 양쪽이 공식적으로 대화 종료에 합의합니다:

  1. FIN 송신: "더 이상 할 말이 없습니다."
  2. ACK 수신: "알겠습니다."
  3. FIN 수신: "저도 더 이상 할 말이 없습니다."
  4. ACK 송신: "알겠습니다. 끝났습니다."

왜 두 단계가 아니라 네 단계일까요? TCP가 전이중(full-duplex) 방식이기 때문입니다—데이터는 양방향으로 독립적으로 흐릅니다. FIN을 보낼 때는 자신의 방향만 닫는 것입니다. 상대방은 아직 보낼 데이터가 있을 수 있습니다. 상대방은 FIN을 확인하고, 데이터 전송을 마친 후, 준비가 되면 자신의 FIN을 보냅니다.

2단계와 3단계 사이에 연결은 반 닫힘(half-closed) 상태가 됩니다. 더 이상 보낼 수 없지만, 여전히 받을 수는 있습니다. 이것이 TCP의 예의 바른 면입니다: 내가 말하기를 끝냈더라도, 상대방이 할 말이 더 있는지 기다립니다.

실제로는 2단계와 3단계가 단일 패킷(ACK+FIN)으로 합쳐지는 경우가 많아, 3방향 종료처럼 보이기도 합니다. 하지만 논리는 그대로입니다: 각 방향은 독립적으로 닫힙니다.

RST: 회선이 끊어지다

RST는 다릅니다. 연결이 RST를 받으면, 즉시 종료됩니다. 확인 응답도, 핸드셰이크도, 하던 일을 마칠 기회도 없습니다. 대기 중인 데이터는 사라지고, 버퍼는 지워집니다. 연결은 그 순간 존재하지 않게 됩니다.

TCP는 특정 상황에서 RST를 보냅니다:

아무도 수신하지 않는 경우. 포트 8080에 연결하려 했지만, 그 포트에서 실행 중인 서비스가 없습니다. 운영 체제는 RST로 응답합니다: "아무도 없습니다." 이것이 포트 스캐너가 닫힌 포트를 식별하는 방법입니다—SYN-ACK 대신 RST를 받습니다.

연결이 존재하지 않는 경우. 이미 닫혔거나 이 기기에 존재한 적 없는 연결에 패킷이 도착합니다. RST는 말합니다: "무슨 말인지 모르겠습니다."

애플리케이션이 충돌한 경우. 또는 명시적으로 연결을 중단한 경우. 소프트웨어가 예기치 않게 종료되면, OS는 열려 있는 모든 연결에 RST를 보내 정리합니다.

자원이 소진된 경우. 시스템이 더 이상 연결을 유지할 수 없습니다. RST는 비상 탈출구입니다.

"연결이 피어에 의해 재설정됨"이 계속 나타나는 이유

이 오류가 보이면, RST를 받은 것입니다. 상대방—또는 상대방과 당신 사이 어딘가—이 이 연결을 즉시 끊어야 한다고 결정한 것입니다. 답답한 점은: 이 오류가 누가 보냈는지, 보냈는지 알려주지 않는다는 것입니다.

거짓말하는 방화벽

방화벽은 종종 목적지에서 온 것처럼 위장하여 RST 패킷을 보냅니다. 서버가 연결을 거부했다고 생각하지만, 실제로는 보이지 않는 중간 장치가 차단한 것입니다.

상태 저장(stateful) 방화벽은 활성 연결 테이블을 유지합니다. 연결이 정책을 위반하거나, 너무 오래 유휴 상태이거나, 의심스러워 보이면, 방화벽은 "서버"에서 보낸 것처럼 RST를 위조하여 당신에게 전송합니다. 서버는 당신이 연결했다는 사실조차 알지 못합니다.

패킷 캡처가 진실을 드러냅니다. RST가 목적지가 아닌 IP에서 왔거나, 해당 호스트의 다른 패킷과 TTL 값이 맞지 않는다면, 보이지 않는 차단자를 찾은 것입니다.

NAT 타임아웃: 조용한 킬러

NAT 장치(라우터, 클라우드 로드 밸런서, 기업용 방화벽)는 상태 테이블에서 연결을 추적합니다. 이 테이블에는 타임아웃이 있습니다—AWS, Azure 같은 클라우드 제공업체는 보통 4~6분이지만, 기업용 장비는 24시간 이상 허용하기도 합니다1.

함정은 이렇습니다: 애플리케이션은 연결이 살아있다고 생각합니다. 서버도 마찬가지입니다. 하지만 중간의 NAT 장치는 그 연결이 존재한다는 것을 이미 잊어버렸습니다. 어느 쪽이든 다음 패킷을 보내면, NAT는 그 패킷을 알아보지 못합니다. 설정에 따라 패킷을 조용히 버리거나 RST를 보냅니다.

데이터베이스 커넥션 풀, 메시지 큐, SSH 세션 같은 장기 연결이 비활성 상태 후 무작위로 끊어지는 이유가 바로 이것입니다. 연결 자체는 정상이었습니다. 중간 장치가 그냥 잊어버린 것입니다.

과부하 상태의 로드 밸런서

로드 밸런서는 백엔드 서버가 사라지거나, 상태 확인(health check)이 실패하거나, 연결 한도를 초과하면 RST를 보냅니다. 이러한 재설정은 배포(백엔드 교체) 시점이나 트래픽 급증(한도 도달) 때 몰리는 경향이 있습니다.

특정 시간대에 집중되는 간헐적인 재설정이 관찰된다면, 그 시점에 인프라에서 무슨 일이 있었는지 살펴보세요.

RST 디버깅: 어디를 봐야 할까

양쪽 끝에서의 패킷 캡처는 RST를 실제로 보낸 곳을 알려줍니다. 출발지 IP, TTL, 타이밍을 비교하세요. RST가 예상치 못한 곳에서 왔다면, 원인을 찾은 것입니다.

애플리케이션 로그는 소프트웨어가 의도적으로 연결을 중단했는지 보여줍니다. 많은 애플리케이션이 RST를 보내는 이유를 기록합니다: 프로토콜 위반, 보안 정책, 타임아웃 초과 등.

방화벽 및 로드 밸런서 로그는 RST를 유발하는 정책 위반, 연결 한도, 상태 확인 실패 내역을 보여줍니다.

TCP 킵얼라이브는 NAT 타임아웃으로 인한 연결 끊김을 방지합니다. 킵얼라이브를 활성화하고, 네트워크 경로에서 가장 짧은 타임아웃보다 짧은 간격으로 설정하세요. 프로브가 중간 장치가 당신을 잊기 전에 유휴 타이머를 재설정합니다.

FIN vs RST: 종료 방법 선택

직접 선택하는 경우는 거의 없습니다. 코드의 소켓 처리 방식이 TCP가 어느 것을 사용할지 결정합니다:

일반 종료 (소켓에서 close() 호출): TCP는 FIN을 보내고 4방향 핸드셰이크를 완료합니다. 전송 중이던 데이터는 전달됩니다. 양쪽이 대화 종료에 합의합니다.

강제 종료 (닫기 전에 SO_LINGER를 0으로 설정): TCP는 즉시 RST를 보냅니다. 핸드셰이크도, 대기도, 대기 중인 데이터가 도착한다는 보장도 없습니다.

거의 모든 경우에 일반 종료를 사용하세요. 강제 종료는 비상 상황을 위해 남겨두세요: 연결이 무효화된 경우, 즉시 자원을 해제해야 하는 경우, 또는 협상 없이 즉시 차단해야 하는 보안 위반이 감지된 경우입니다.

TCP FIN 및 RST에 관한 자주 묻는 질문

서버가 정상인데 왜 "연결이 피어에 의해 재설정됨" 오류가 발생하나요?

재설정은 서버에서 오지 않는 경우가 많습니다. 방화벽, 로드 밸런서, NAT 장치가 목적지에서 보낸 것처럼 보이는 RST 패킷을 전송합니다. 양쪽 끝에서 패킷을 캡처해 보세요—서버가 RST를 보내지 않았다면, 중간 어딘가가 보낸 것입니다.

NAT 타임아웃이 장기 연결을 끊는 것을 어떻게 방지하나요?

NAT 장치의 타임아웃보다 짧은 간격으로 TCP 킵얼라이브를 활성화하세요. 클라우드 환경에서는 보통 4분 미만의 간격이 필요합니다. 킵얼라이브 프로브가 유휴 타이머를 재설정하여, NAT가 연결 존재를 잊지 않게 해줍니다.

연결을 더 빨리 닫기 위해 RST를 사용해야 하나요?

아니요. RST는 전달을 보장하지 않습니다—전송 중이던 데이터가 손실될 수 있고, 상대방은 아무런 경고도 받지 못합니다. 연결이 무효화되거나 위험해진 경우가 아니라면 일반 종료(FIN)를 사용하세요.

포트 스캔 결과에서 포트가 "닫힘"과 "필터링됨"으로 표시되는 것은 무슨 의미인가요?

닫힌 포트는 RST로 응답합니다—네트워크 스택은 동작 중이지만, 해당 포트에 바인딩된 서비스가 없는 상태입니다. 필터링된 포트는 응답이 없습니다—방화벽이 프로브를 조용히 차단했습니다. 둘 다 연결할 수 없다는 점은 같지만, 서로 다른 네트워크 구성을 드러냅니다.

출처

Ήταν χρήσιμη αυτή η σελίδα;

😔
🤨
😃
TCP FIN vs. RST: 정상 종료 vs. 강제 종료 • Βιβλιοθήκη • Connected