Java 네트워크 예외 — 연결 예외 정리

네트워크 프로그래밍에서 예외는 크게 두 범주로 나뉜다. 연결 자체가 불가능한 경우와, 연결은 시도됐지만 실패하는 경우다. 이번 글에서는 소켓 연결 시 발생하는 대표적인 예외 세 가지를 코드와 함께 정리한다

예제 코드

import java.io.IOException;
import java.net.ConnectException;
import java.net.Socket;
import java.net.UnknownHostException;

public class ConnectMain {
    public static void main(String[] args) throws IOException {
        unknownHostEx1();
        unknownHostEx2();
        connectionRefused();
    }

    private static void unknownHostEx1() throws IOException {
        try {
            Socket socket = new Socket("999.999.999.999", 80);
        } catch (UnknownHostException e) {
            e.printStackTrace();
        }
    }

    private static void unknownHostEx2() throws IOException {
        try {
            Socket socket = new Socket("google.gogo", 80);
        } catch (UnknownHostException e) {
            e.printStackTrace();
        }
    }

    private static void connectionRefused() throws IOException {
        try {
            Socket socket = new Socket("localhost", 45678);
        } catch (ConnectException e) {
            e.printStackTrace();
        }
    }
}

**실행 결과**

java.net.UnknownHostException: 999.999.999.999
java.net.UnknownHostException: google.gogo
java.net.ConnectException: Connection refused

UnknownHostException

호스트 자체를 알 수 없을 때 발생한다. TCP연결 시도 이전 단계에서 실패하는 경우이다

케이스 1 – 존재하지 않는 IP 대역

Socket socket = new Socket("999.999.999.999", 80);

999.999.999.999는 존재하지 않는 IP 대역이다. IP 형식은 맞지만 실제로 할당된 대역이 없기 때문에 연결 시도 자체가 불가능하다

케이스 2 – 존재하지 않는 도메인

Socket socket = new Socket("google.gogo", 80);

google.gogo라는 도메인은 DNS에 등록되어 있지 않다. 자바는 도메인을 IP로 변환하기 위해 DNS를 조회하는데, 조회 결과가 없으면 UnknownHostException을 던진다

두 케이스 모두 네트워크 패킷이 목적지에도 도달하기 전에 실패한다는 점이 공통점이다

ConnectException: Connection refused

Socket socket = new Socket("localhost", 45678);

이 예외는 앞의 UnknownHostException와는 성격이 다르다. “거절(refused)”이라는 단어에 주목할 필요가 있다. 거절은 상대방이 메시지를 받았다는 뜻이다

동작 원리

  • localhost (내 컴퓨터)는 커져 있으므로 TCP 연결 패킷이 목적지까지 도달한다
  • 서버 컴퓨터의 OS가 패킷을 수신하고 포트를 확인한다
  • 45678 포트를 사용 중인 프로세스가 없으므로 OS가 연결을 거절한다
  • OS는 클라이언트에게 TCP RST(Reset) 패킷을 전송한다
  • 클라이언트는 RST 패킷을 수신하고 ConnectException: Connection refused를 던진다

TCP RST 패킷은 “이 연결은 비정상이므로 즉시 해제하라”는 신호다. RST를 받은 쪽은 추가 처리 없이 바로 연결을 해제해야 한다
Connection refused가 발생하는 상황은 크게 두 가지다. 서버 IP는 유효하지만 해당 포트를 사용하는 프로세스가 없는 경우, 그리고 방화벽이 허용되지 않는 IP의 접근을 차단하는 경우다

두 예외의 핵심

구분UnknownHostExceptionConnectException
발생 지점DNS 조회 또는 IP 검증 단계TCP 연결 시도 단계
패킷 전송 여부전송 안 됨전송됨
원인잘못된 IP 대역 또는 없는 도메인포트 미사용, 방화벽 차단
서버 도달 여부도달 안 됨서버 OS까지 도달함

연결 예외를 만났을 때 방법은 명확하다. UnknownHostException 이면 호스트 주소 자체를 확인한다. IP 대역이 올바른지, 도메인이 실제로 존재하는지 점검한다. ConnectException: Connection refused이면 서버까지는 도달한 것이다. 서버 프로세스가 실행 중인지, 해당 포트로 리스닝 중인지, 방화벽 규칙이차단하고 있지는 않은지 확인한다

출처 – 김영한 님의 강의 중 김영한의 실전 자바 – 고급 2편, I/O, 네트워크, 리플렉션