728x90
문제 발생
- 우테캠에서 주어진 과제인 WAS 만들기를 하다가 콘솔에 하나의 요청에 대한 응답이 가기 전에 추가로 커넥션이 맺어지는 것을 목격했다.
- 빨리 기능 구현을 해야 하지만, 나는 무슨 이유로 이런 현상이 발생하는지 궁금했고, 궁금한 건 또 못 참기에 삽질을 시작했다
소켓 커넥션에서 발생한 이중 연결 요청
- 이상했던 점은 커넥션을 맺은 후, 아무런 데이터를 보내지 않는다는 것이다. 그래서 처음에 Inputstream으로 데이터를 읽으면 아무것도 읽지 못한다.
- 근데 그걸 에러로 응답해도 브라우저는 신경 쓰지 않고 다음에 오는 에러 응답만을 받아서 렌더링을 한다..
- 근데 이거 불가능하지 않나..? 뭔지는 모르겠지만 첫 커넥션에 대한 응답은 브라우저가 받질 않는 것 같다..?
- 이게 정상 요청일 때도 이렇게 동작해서 브라우저는 에러로 인식을 하지 않는다. 왜 그럴까..?
- 뭔가 따닥!처럼 동작하는데 요청과 응답에는 별 문제가 없다.
- 파이프라이닝이 1.1부터 추가되어서 응답이 오기 전에 요청을 보낼 수 있다는데 이것 때문인가..?
- 그래서 에러가 발생했지만, 파비콘이 받아진 적도 있었으니 가능성이 있지 않을까?
- 그런 요청들이 추가적으로 들어와서 connection이 생성된 것이 아닐까?
와이어 샤크
- 하.. 졸업하고 다시는 설치 안 할 것 같았지만, 내 의문점을 해결하려면 결국 패킷을 까봐야 한다고 생각해서 설치했다.
- 여기 보면 http 통신을 하기 전에 TCP 커넥션을 생성하며 서로의 정책을 주고받는듯한 행위를 한다.
- 이후 tcp window update를 보니 핸드 셰이크를 하며 정책을 주고받고 업데이트를 한 것으로 보인다.
- 근데 이걸 서버가 연결을 받았고, 데이터를 처리하는 과정에서 HTTP 메서드를 보내지 않았고, 난 이걸 405로 처리했기 때문에 405 응답이 온 것으로 보인다.
- 의문점은 내가 알기론 tcp window update를 한다고 해서 커넥션을 종료하거나 새로 보내진 않을 텐데..
- 그래서 이를 위해 따로 클라이언트 소켓만 연결하는 코드로 데이터를 보내 보았다.
public void sendSocketData(String data) {
Socket socket = null;
try {
System.out.println("Client: Connecting");
socket = new Socket("127.0.0.1", 8080);
System.out.println("Client: Connect status = " + socket.isConnected());
Thread.sleep(1000);
OutputStream stream = socket.getOutputStream();
BufferedOutputStream out = new BufferedOutputStream(stream);
byte[] bytes = data.getBytes();
out.write(bytes);
System.out.println("Client: Sent data = " + data);
out.close();
} catch (Exception e) {
e.printStackTrace();
}finally {
if (socket != null) {
try {
socket.close();
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
- 정말 단순하게 커넥션 후 파라미터로 받은 data를 쓰기만 했다.
- 참고로 코드는 옛날에 자바 공부하며 소켓 코드 잠깐 작성해 본 것을 그대로 활용했다.
- 서버에서는 소켓 연결을 했으니 연결이 됐다는 로그가 보였고, 브라우저를 사용할 때처럼 추가로 연결됐다는 응답이 보이지 않았다.
- 와이어 샤크를 통해 패킷이 어떻게 전송됐는지 살펴보았다.
- 처음에 연결을 위한 핸드셰이크 과정이 보이고(SYN → SYN, ACK → ACK), 이후에 58836→8080으로 보내는 요청이 보인다. 즉, data를 전송한 것이다. 이에 대한 응답으로 서버가 ACK를 보내줬고, 내 서버의 코드는 해당 데이터를 읽을 수 있지만, 파싱 하는 과정에서 메서드 정보가 누락되었기에 405 에러가 발생했다.
- 추가로 아무 데이터도 보내지 않고 연결을 끊었을 때도, 서버는 Inputstream으로 기다리고 있고, 데이터가 오지 않으니 405 에러를 응답했다.
- 사실 여기까지 보면 내가 메서드를 안보내면 405를 응답하게 했으니 당연하다..
- 다시 돌아와서 브라우저로 요청을 보내고 패킷을 캡처해 보면
- 내가 생각하는 게 맞다면, 위 과정에서 처음 61171 → 8080으로 핸드셰이크를 시작하는 세 줄이 있고, 이후 TCP window를 업데이트하게 된다.
- 이후에 다른 포트로 8080에 요청을 보내는 것을 볼 수 있다. 브라우저는 html하나만 요청하는 게 아니라 css, js 등 다양한 것을 요청하니 그걸 다른 포트로 보낼 수 있다고 한다. 아마 그런 작업이 아닐까..?라는 생각이다.
- 그럼 데이터는 보내줘야 하는데, 아무런 데이터는 보내지 않는다.. 그럼 다른 요청이 오는 것도 아닐 텐데..
- 그리고 내가 의도적으로 잘못 보낸 index.htm를 달라는 HTTP 요청을 보내고 서버가 64398로 ack를 통해 응답했다.
- 이후 405 에러가 서버로부터 반환되고(이건 클라이언트에서 아무 데이터를 보내지 않아서 어떤 메서드를 사용했는지 판단할 수 없어 발생한 에러이다. inputStream.read()를 통해 나온 결과가 -1이었기 때문), RST 패킷을 64397 클라이언트가 보내는데, 이건 클라이언트가 연결을 강제로 종료하겠다는 의미이다.
- 사실 이 부분이 가장 의문인 게 여러 데이터를 각각 따로 요청을 했는데 왜 아무 데이터를 보내지 않는지 모르겠다.
- 여하튼 아무것도 보내지 않아 서버에서는 http 응답을 보내고, 클라이언트는 http로 무언가를 보내지 않았기에 405 에러를 렌더링 하지 않는 것으로 보인다.
- 더 이상한 건 첫 요청이 html 문서를 달라는 요청이 아닌 것이다. 서버와 커넥션을 맺고 아무것도 안 하는 이 녀석의 정체가 뭘까..?
- window update도 상관이 없는 게 코드로 만든 소켓에서는 새로운 포트로 연결하는 과정이 없었다.
- 마지막으로 서버는 index.htm이란 파일이 없으므로 404 응답을 하고, 이때는 64398 클라이언트도 http로 요청을 보냈기에 이를 받아서 404 화면을 응답했다.
- 브라우저가 왜 요청을 두 번 보내는지는 의문이다. 포스트맨도 두 번 보낸다..
- 터미널에서 [curl localhost:8080/index.htm] 명령어를 사용한 결과인데, 터미널은 또 하나의 응답만 잘 보낸다.
- 그렇다면 브라우저 문제 같은데 왜 그런지 잘 모르겠다..
- 패킷을 본 결과는 브라우저에 문제가 있다고 생각했고, 하루 종일 삽질을 한 결과 문제를 찾았다.. 결과는 조금 충격적이었는데, 개발자 도구를 사용하면 추가 요청이 갈 수 있다는 것이다. 이게 네트워크 상태를 모니터링하거나 필요한 리소스들이 잘 오는지 확인하거나 디버깅이나 검사할 때 추가적인 요청을 보낼 수 있다고 한다.
- 사실 지금 와서 생각하면 네트워크를 통해 주고받은 데이터를 개발자 도구를 통해 확인할 수 있고, 그 의미는 서버가 살아는 있는지 확인해야 할 테니까 어떻게 보면 당연하다..
- 어쨌든 개발자 도구를 닫고 요청을 보내니 아래처럼 하나의 요청과 응답만 도착하였다
- 하루종일 왜 연결이 이중으로 오는지 찾았는데 결과가 허무하다..
- 어쨌든 개발자 도구가 네트워크 상태를 확인하기 위해 뭔가 패킷을 보낸 것으로 보이고, 당연히 데이터를 보내지 않았을 것이고, 그에 대한 응답도 개발자도구가 받았으니 화면에 렌더링 하지 않았던 것이었다..
- 그래도 CS 스터디를 나름 열심히 했기에 패킷 혼자서 볼 생각도 했고, 결국엔 브라우저가 문제라는 생각을 했으니 긍정적으로 생각하자
728x90
'Network' 카테고리의 다른 글
로드밸런싱(Load Balancing)과 Nginx (0) | 2023.04.18 |
---|