Recent Posts
Recent Comments
«   2024/12   »
1 2 3 4 5 6 7
8 9 10 11 12 13 14
15 16 17 18 19 20 21
22 23 24 25 26 27 28
29 30 31
관리 메뉴

ㅇ.ㅇ

[WebSocket] Error - The remote endpoint was in state [TEXT_FULL_WRITING] which is an invalid state for called method 본문

Error

[WebSocket] Error - The remote endpoint was in state [TEXT_FULL_WRITING] which is an invalid state for called method

yun_ 2023. 5. 20. 16:59
반응형

 

 

소켓통신을 구현하여 broadcasting으로 클라이언트에게 데이터를 보내는 중 json 형식이 깨지는 것을 발견하였고, 그것을 수정하는 과정에서 에러가 발생하였다. 막상 타입문제는 아니었지만 함께 적어본다.

상황

웹소켓에서 스케줄링을 하면서 브로드캐스팅으로 계속 데이터를 보내고 있는 상황이다. 그렇지만 여러 스케줄러가 하나의  'broadcast' 메소드를 호출하면서 충돌이 난 것을 확인하게 되었다.

에러 내용

The remote endpoint was in state [TEXT_FULL_WRITING] which is an invalid state for called method

문제 원인

스레드 동기화

해당 에러는 원격서비스가 현재 텍스트 작성중인 상태로  '호출하려는 메서드에는 유효하지 않은 상태'인 경우에 발생한다. 즉 이미 어떠한 상태에 있으므로 다시 메서드가 호출을 하지 않는다는 것.

// 수정 전 코드

	public static void broadcast(Session session, Object object) throws JsonProcessingException {

        try {
                if (session != null && session.isOpen()) {
                    session.getBasicRemote().sendText(object);
                }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

쉽게말하면, 에러가 발생하는 이유 자체가 여러 스레드가 동시에 session.getBasicRemote().sendText(object) 메서드를 호출하는 동안, 해당 세션 객체가 이미 '텍스트 작성중인 상태'에 있어서 호출된 메서드에 대한 유효하지 않은 상태로 간주되기 때문이다. 

이런 문제를 해결하기 위해서는 '세션 객체 동기화'를 해주어야 한다. 세션 객체를 동기화해주면 여러 스레드나 프로세스가 동시에 하나의 세션을 호출하지 못하도록 제어할 수 있다.

해결 방법

이 문제를 해결하기 위해서는 lock이라는 객체를 사용해서 동기화를 수행한다. 
모든 스레드는 lock 객체에 대한 동기화를 기다려야하므로, 여러 세션 객체에 대한 동기화 문제를 방지할 수 있다.

// 수정 후 코드

    private static final Object lock = new Object();

    public static void broadcast(Session session, Object object) throws JsonProcessingException {

        try {
            synchronized (lock) { // lock 객체를 사용한 동기화
                if (session != null && session.isOpen()) {
                    session.getBasicRemote().sendText(json);
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
  • synchronized (lock) {... } : lock 객체를 사용해서 동기화된 블록을 생성
    • 여러 스레드가 동시에 sendText() 메서드 호출해도, lock 객체를 사용한 동기화로 인해 하나의 세션에 대한 작업이 완료 된 후에 다음 세션으로 이동

 

+ ) Java String 타입을 Json형식으로 변환

브로드캐스팅하는 메소드로 데이터는 String 타입으로 받아와진다.

예 ) {name=admin}

그렇지만 클라이언트 쪽에다가는 아래와 같이 json 형식으로 변환해서 보내야했다. 

예 ) {"name":"admin"}

그래서 아래와 같은 식으로 Jackson 라이브러리의 ObjectMapper를 사용하여 Java 객체를 Json 문자열로 직렬화하였다. 

- ObjectMapper : Java 객체와 Json 데이터 간의 변환 처리
- writeValueAsString() : 주어진 객체를 Json 문자열로 직렬화

ObjectMapper objectMapper = new ObjectMapper();
String json = objectMapper.writeValueAsString(object);

session.getBasicRemote().sendText(json);

 

반응형