반응형

Java를 이용해 웹 서버를 구현하던 중 신기한 현상(?)을 발견해 그 내용과 해결 방법을 공유해보려 한다.

Context

Java에서 서버소켓을 생성하기 위해선 ServerSocket 클래스가 사용된다.

int port = 8080;
try (ServerSocket listenSocket = new ServerSocket(port)) {
    log.info("Web Application Server started {} port.", port);

    // 클라이언트가 연결될때까지 대기한다.
    Socket connection;
    while ((connection = listenSocket.accept()) != null) {
        // Do something ..
    }
}

그리고 해당 서버에 액세스하는 방법은 크게 3가지 존재한다

  1. http://localhost:8080
  2. http://127.0.0.1:8080
  3. http://[::1]:8080

2번은 IPv4, 3번은 IPv6이다. 그럼 1번은 둘 중 어떤걸로 동작하는걸까? 시스템의 기본 값?

확인해보기 위해서는 실제로 요청을 보내볼 필요가 있다. 그리고 조금 더 편하게 요청을 보내기 위해 Postman을 사용했다.

Postman GET Request

그런데 분명 요청을 1번 보냈음에도 서버에는 2번의 요청이 수신되었다. 크롬 브라우저처럼 /favicon.ico에 대한 요청을 보내는 것인가? 라는 생각이 들어 RequestHeader를 파싱하여 확인해보았는데...

첫 번째 요청. RequestHeader가 null이다
두 번째 요청. 내가 보냈던 요청에 대한 RequestHeader이다.

첫 번째는 요청에는 Header가 담겨있지 않았으며, 두 번째 요청은 내가 의도했던 Header가 담겨있었다.

그렇다면  첫 번째 요청은 무엇인가?

의문의 첫 번째 요청

내가 구현한 서버의 문제인지, Postman의 요청인지 알 수 없어 우선 nc로 port를 열고 Postman으로 동일한 요청을 보내봤다. 그런데 정상적으로 잘 동작한다. (내가 서버를 잘못 구현한건가?)

이해가 되지 않아 와이어샤크를 이용해 전송되는 패킷을 확인해보았다.

응? SYN...? IPv6...?

문제의 발견

그렇다. Postman은 localhost로 요청을 보낼 때 IPv6로 SYN을 먼저 보내고 그 후 IPv4로 Connection하는 방식을 사용하는 것이다.

도대체 왜? 그냥 시스템의 기본 설정으로 동작하게끔 해주면 안되나? 검색을 하다보니 나와 비슷한 생각을 가진 사람이 있었다.

https://github.com/pocoproject/poco/issues/2605

 

Default ServerSocket uses IPv4 only, StreamSocket defaults to IPv6 · Issue #2605 · pocoproject/poco

Expected behavior When I create a server (based on TCPServer) and client (using StreamSocket), I expected that they can connect without any issues or further configuration required. Actual behavior...

github.com

ServerSocket 을 이용해 접속을 대기하는데, IPv6 요청이 들어와 Connection Fail이 발생한다는 내용이다.

댓글에는 대부분의 Api Client가 IPv6로 SYN을 보내고 IPv4 Connection 하고 있으며 이를 해결하기 위해선 ServerSocket 생성자에 바인드에 사용할 IP를 명시적으로 작성해주라는 조언이 남겨져 있었다.


아니, 이렇게까지 처리를 해줘야 한다고? 그럼 다른 웹 서버 프레임워크는 어떻게 동작하는거지? 

스프링 프레임워크의 동작을 한번 살펴보자.

아.. 얘네도 명시적으로 IP를 지정해주는구나.. 근데 localhost로 지정해주네? 이건 InetAddress.getByName 메소드가 시스템에서 사용하는 IP 버전을 가져오게끔 설정되어 있기 때문이다. 즉 localhost의 IPv4에 대응되는 127.0.0.1으로 설정되는 것이다.

그래서 나도 내 서버의 구현을 위와 같은 형태로 수정했고, 정상적으로 IPv4에 대응되는 요청만 받아낼 수 있었다.


추측으로는 IPv6로 SYN을 보내 연결이 가능한지 확인한 후 가능하다면 IPv6로 연결하고 그렇지 않다면 IPv4로 연결하는 것 같다.

그래도 확실하지 않고 여전히 의문이기에 관련된 내용을 Postman App Support Issues에 질문으로 작성해두었다. 언젠가 답변이 도착하면 업데이트 해두려 한다.

https://github.com/postmanlabs/postman-app-support/issues/10221

 

When i requested to 'localhost' host, the Postman sends SYN to IPv6, and then connects to IPv4. · Issue #10221 · postmanlabs/p

Is there an existing issue for this? I have searched the existing issues Describe the Issue When I send a requested to 'localhost' host, it attempts to connect without having a Request Head...

github.com

 

반응형

'Web' 카테고리의 다른 글

localhost는 IPv6와 IPv4 어떤걸로 동작할까?  (0) 2021.08.16
잘못된 내용이 있다면, 다른분들에게 전달되지 않게끔 댓글로 지적 부탁드립니다!

+ Recent posts