복붙노트

[SPRING] Spring WebSocket @SendToSession : 특정 세션에 메시지 보내기

SPRING

Spring WebSocket @SendToSession : 특정 세션에 메시지 보내기

특정 세션에 메시지를 보낼 수 있습니까?

클라이언트와 스프링 서블릿 사이에 인증되지 않은 websocket이 있습니다. 비동기 작업이 끝나면 특정 연결에 원치 않는 메시지를 보내야합니다.

@Controller
public class WebsocketTest {


     @Autowired
    public SimpMessageSendingOperations messagingTemplate;

    ExecutorService executor = Executors.newSingleThreadExecutor();

    @MessageMapping("/start")
    public void start(SimpMessageHeaderAccessor accessor) throws Exception {
        String applicantId=accessor.getSessionId();        
        executor.submit(() -> {
            //... slow job
            jobEnd(applicantId);
        });
    }

    public void jobEnd(String sessionId){
        messagingTemplate.convertAndSend("/queue/jobend"); //how to send only to that session?
    }
}

이 코드에서 알 수 있듯이 클라이언트는 비동기 작업을 시작할 수 있으며 완료되면 종료 메시지가 필요합니다. 분명히, 나는 신청자에게만 메시지를 보내고 모든 사람에게 방송 할 필요는 없다. @SendToSession 어노테이션 또는 messagingTemplate.convertAndSendToSession 메소드를 갖는 것이 좋습니다.

최신 정보

나는 이것을 시도했다 :

messagingTemplate.convertAndSend("/queue/jobend", true, Collections.singletonMap(SimpMessageHeaderAccessor.SESSION_ID_HEADER, sessionId));

그러나 이것은 지정된 세션뿐만 아니라 모든 세션으로 브로드 캐스팅됩니다.

업데이트 2

convertAndSendToUser () 메소드로 테스트하십시오. 이 테스트는 공식 Spring 튜토리얼의 해킹이다 : https://spring.io/guides/gs/messaging-stomp-webocket/

다음은 서버 코드입니다.

@Controller
public class WebsocketTest {

    @PostConstruct
    public void init(){
        ScheduledExecutorService statusTimerExecutor=Executors.newSingleThreadScheduledExecutor();
        statusTimerExecutor.scheduleAtFixedRate(new Runnable() {                
            @Override
            public void run() {
                messagingTemplate.convertAndSendToUser("1","/queue/test", new Return("test"));
            }
        }, 5000,5000, TimeUnit.MILLISECONDS);
    } 

     @Autowired
        public SimpMessageSendingOperations messagingTemplate;
}

이것은 클라이언트 코드입니다.

function connect() {
            var socket = new WebSocket('ws://localhost:8080/hello');
            stompClient = Stomp.over(socket);
            stompClient.connect({}, function(frame) {
                setConnected(true);
                console.log('Connected: ' + frame);
                stompClient.subscribe('/user/queue/test', function(greeting){
                    console.log(JSON.parse(greeting.body));
                });
            });
        }

불행하게도 클라이언트는 예상대로 세션 당 회신을 5000ms마다받지 못합니다. SimpMessageHeaderAccessor.getSessionId ()를 사용하여 디버그 모드에서 볼 수 있기 때문에 "1"은 연결된 두 번째 클라이언트에 대한 유효한 sessionIf입니다.

배경 시나리오

원격 작업의 진행률 표시 줄을 만들고 클라이언트가 서버에 비동기 작업을 요청하고 서버에서 보낸 websocket 메시지로 진행 상황을 확인합니다. 이것은 파일 업로드가 아니라 원격 계산이므로 서버 만 각 작업의 진행 상태를 압니다. 각 작업은 세션별로 시작되므로 특정 세션에 메시지를 보내야합니다. 클라이언트가 원격 계산을 요청합니다. 서버는이 작업을 시작하고 모든 작업 단계에 대해 신청자 클라이언트에게 작업 진행 상태로 응답합니다. 클라이언트는 작업에 대한 메시지를 받고 진행률 / 상태 표시 줄을 만듭니다. 이것이 내가 세션 당 메시지가 필요한 이유입니다. 또한 사용자 별 메시지를 사용할 수도 있지만 Spring은 사용자 별 요청하지 않은 메시지를 제공하지 않습니다. (Spring Websocket으로 사용자 메시지를 보낼 수 없다)

해결 방법

 __      __ ___   ___  _  __ ___  _  _   ___      ___   ___   _    _   _  _____  ___  ___   _  _ 
 \ \    / // _ \ | _ \| |/ /|_ _|| \| | / __|    / __| / _ \ | |  | | | ||_   _||_ _|/ _ \ | \| |
  \ \/\/ /| (_) ||   /| ' <  | | | .` || (_ |    \__ \| (_) || |__| |_| |  | |   | || (_) || .` |
   \_/\_/  \___/ |_|_\|_|\_\|___||_|\_| \___|    |___/ \___/ |____|\___/   |_|  |___|\___/ |_|\_|

UPDATE2 솔루션에서 시작하여 마지막 param (MessageHeaders)을 사용하여 convertAndSendToUser 메서드를 완료해야했습니다.

messagingTemplate.convertAndSendToUser("1","/queue/test", new Return("test"), createHeaders("1"));

createHeaders ()는이 메소드입니다.

private MessageHeaders createHeaders(String sessionId) {
        SimpMessageHeaderAccessor headerAccessor = SimpMessageHeaderAccessor.create(SimpMessageType.MESSAGE);
        headerAccessor.setSessionId(sessionId);
        headerAccessor.setLeaveMutable(true);
        return headerAccessor.getMessageHeaders();
    }

해결법

  1. ==============================

    1.특정 목적지를 만들 필요가 없으며, 이미 봄 4.1 (SPR-11309 참조)에서 즉시 구현됩니다.

    특정 목적지를 만들 필요가 없으며, 이미 봄 4.1 (SPR-11309 참조)에서 즉시 구현됩니다.

    사용자가 / user / queue / something 대기열에 가입하면 다음을 사용하여 단일 세션으로 메시지를 보낼 수 있습니다.

    SimpMessageSendingOperations Javadoc에 명시된 것처럼 사용자 이름은 실제로 sessionId이므로이를 헤더로 설정해야합니다. 그렇지 않으면 DefaultUserDestinationResolver가 메시지를 라우트 할 수 없으며 드롭합니다.

    SimpMessageHeaderAccessor headerAccessor = SimpMessageHeaderAccessor
        .create(SimpMessageType.MESSAGE);
    headerAccessor.setSessionId(sessionId);
    headerAccessor.setLeaveMutable(true);
    
    messagingTemplate.convertAndSendToUser(sessionId,"/queue/something", payload, 
        headerAccessor.getMessageHeaders());
    

    이를 위해 사용자를 인증 할 필요는 없습니다.

  2. ==============================

    2.그것은 매우 복잡하고 제 의견으로는 가치가 없습니다. 세션 ID별로 모든 사용자 (심지어 인증되지 않은 사용자)에 대한 구독을 만들어야합니다.

    그것은 매우 복잡하고 제 의견으로는 가치가 없습니다. 세션 ID별로 모든 사용자 (심지어 인증되지 않은 사용자)에 대한 구독을 만들어야합니다.

    모든 사용자가 자신 만의 고유 한 대기열에 가입한다고 가정 해 보겠습니다.

    stompClient.subscribe('/session/specific' + uuid, handler);
    

    서버에서 사용자가 가입하기 전에 특정 세션에 대한 메시지를 보내고 메시지를 보내 맵에 저장해야합니다.

        @MessageMapping("/putAnonymousSession/{sessionId}")
        public void start(@DestinationVariable sessionId) throws Exception {
            anonymousUserSession.put(key, sessionId);
        }
    

    그 후에 사용자에게 메시지를 보내려면 다음을 수행해야합니다.

    messagingTemplate.convertAndSend("/session/specific" + key); 
    

    그러나 나는 당신이 무엇을하려고하는지 그리고 어떻게 특정 세션을 찾을 것인지 (누가 익명인지)를 모른다.

  3. ==============================

    3.세션 ID를

    세션 ID를

    노트 : 그다지 잊지 말고 '/ 사용자'를 서버 측의 엔드 포인트에 추가하십시오.

  4. from https://stackoverflow.com/questions/34929578/spring-websocket-sendtosession-send-message-to-specific-session by cc-by-sa and MIT license