1. Connection Reset By Peer가 뭐지?


서버 로그를 보다 보면 가끔 이런 에러가 찍힌다.

java.io.IOException: Connection reset by peer

매번 터지는 것도 아니고, 금방 복구되는 경우가 많아서 “상대방 서버가 잠깐 불안정했나보다” 하고 넘어가곤 했다. 그런데 문득 의문이 생겼다. 상대 서버가 재배포되면 기존 커넥션들이 다 끊길 텐데, 왜 모든 요청이 실패하지 않고 일부만 간헐적으로 실패하는걸까? 이 글에서는 이 질문으로부터 시작해서 알아본 내용들에 대해 정리한다.

2. TCP가 연결을 끊는 두 가지 방법


Connection Reset By Peer 라는 에러 이름에 힌트가 있다. 상대방(peer)이 연결을 강제로 초기화(reset) 했다는 뜻인데, 이걸 이해하려면 TCP가 연결을 끊는 방식부터 알아야 한다.

2.1 FIN - 정상 종료

TCP 연결은 보통 4-way handshake 라고 불리는 과정을 통해 종료한다.

Client ──FIN──► Server    (나 이제 보낼 것 없어)
Client ◄──ACK── Server    (알겠어)
Client ◄──FIN── Server    (나도 보낼 것 없어)
Client ──ACK──► Server    (알겠어)

이는 마치 전화를 끊기 전에 “나 이제 끊는다. 할말 없지?” 라고 친절하게 묻는 것과 같다.

2.2 RST - 강제 종료

FIN 방식과 달리 RST는 협의 없이 연결을 즉각 끊는 방식이다. 4-way handshake 같은 과정 없이 RST 패킷 하나로 끝난다.

RST를 보내는 주체는 OS(커널)일 수도 있고, 애플리케이션일 수도 있다. 이미 닫힌 소켓에 데이터가 들어오면 커널이 자동으로 RST를 보내고, 프로세스가 SIGKILL로 강제 종료되면 커널이 열린 소켓들을 일괄 정리하면서 RST를 전송한다. 또는 애플리케이션이 특정 옵션을 설정한 뒤 소켓을 close() 하면 의도적으로 RST를 보낼 수도 있다.

2.3 에러는 끊는 순간이 아니라, 소켓에 뭔가를 시도하는 순간에 터진다.