개발일지/트러블슈팅

웹 소켓을 닫은 상태에서 메시지 수신 관련 문제 해결

khw7385 2025. 6. 19. 22:52

문제 상황

스프링 프레임워크의 WebSocketHandler 를 이용하여 웹 소켓 서버를 구현하는 과정 중 에러 발생

public void afterConnectionEstablished(WebSocketSession session) 메서드 오버라이딩 ⇒ 쿼리 파라미터를 파싱하여 얻은 값을 이용하여 프롬프트 조회 시 예외 처리 ⇒ 예외 발생 시 웹 소켓 close 호출 및 자원 정리

close 를 한 상황임에도 handleTextMessage 클라이언트로부터 받은 메시지를 처리하는 함수 호출 ⇒ 정리된 자원에 접근하여 예외 발생!!!

원인 분석

afterConnectionEstablished 호출 시점

웹 소켓 handshake 가 완료되면 해당 메서드가 호출됨

클라이언트-서버의 연결 과정

자료를 찾아보다보니 무거워진 경향이 있으나 지금 차례에 정리해본다.

NIO Connector 를 기준으로 설명을 한다.

NIO Connector가 새로 들어온 소켓을 받아들이고, 실제 I/O 처리를 Worker 쓰레드 풀에 위임한다.

  1. Accpetor 컴포넌트가 serverSocket.accept()를 통해 Socket Connection 을 accept 한다.
  2. accept 의 결과를 얻은 Socket Channel 을 톰캣의 NIO Channel 로 NIO Channel 을 PollerEvent 로 캡슐환 후 PollerEvent 큐에 퍼블링싱한다. (Acceptor)
  3. Poller 가 PollerEvent 를 꺼내 Poller 내부의 Selector 에 채널 등록
  4. Selector 가 Channel 을 감시하여 이벤트 발생(read, write) 감지 시 요청을 처리하는 SocketProcessor 를 execututor에 제출하여 Worker 쓰레드에 할당
  5. 이 때, 톰캣의 Poller 에 의해 Task이 ThreadPoolExecutor 로 생산(Produce)되면, 내부 쓰레드가 소비(Consume) 한다.

결론

afterConnectionEstablished 를 수행하는 과정에서 클라이언트로부터 음성 데이터가 입력이 되어 이미 Task Queue 에 생산(Produce)된 상황이기 때문에 afterConnectionEstablished 에서 소켓을 닫더라도 handleMessage 메서드가 호출된다.

해결

클라이언트로부터 입력된 메시지를 처리할 때는 소켓이 열려(Open) 있는지 검사한 후 처리를 한다.

 

 

참고자료

https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/web/socket/WebSocketHandler.html

 

WebSocketHandler (Spring Framework 6.2.8 API)

Invoked after the WebSocket connection has been closed by either side, or after a transport error has occurred.

docs.spring.io

https://mangkyu.tistory.com/422

 

[SpringBoot] 멀티 스레드 기반으로 다중 요청을 처리하는 톰캣(Tomcat)의 구조와 동작 방식

1. 멀티 스레드 기반으로 다중 요청을 처리하는 톰캣(Tomcat)의 구조와 동작 방식[ 웹 애플리케이션 서버(WAS, Web Application Server)과 톰캣 ]스프링 MVC 프레임워크는 자바 엔터프라이즈 개발을 편리하

mangkyu.tistory.com

https://jh-labs.tistory.com/329

 

[Tomcat] 아파치 톰캣9 Socket I/O 동작방식 이해

아파치 Tomcat의 BIO Connector / NIO Connector 개념 1) BIO Connector - Response를 보내고 끝내는 것이 아니라 TCP Connection이 만료될 때까지 Thread가 활성 상태로 남아있으며 소켓이 닫히면 Pool로 반환되는 구조 -

jh-labs.tistory.com