检测SessionUnsubscribeEvent

时间:2019-02-12 20:37:26

标签: java spring websocket spring-websocket

我的处境

我正在建立一个小型Web聊天,以了解Spring和Spring WebSocket。您可以创建不同的房间,每个房间在/topic/room/{id}都有自己的频道。

我的目标是检测用户何时加入和离开聊天室,我认为我可以为此使用Spring WebSocket的SessionSubscribeEventSessionUnsubscribeEvent

SessionSubscribeEvent获取目的地很简单:

@EventListener
public void handleSubscribe(final SessionSubscribeEvent event) {
    final String destination = 
            SimpMessageHeaderAccessor.wrap(event.getMessage()).getDestination();

    //...
}

但是,在以下代码段中,SessionUnsubscribeEvent似乎没有传送目标频道,destinationnull

@EventListener
public void handleUnsubscribe(final SessionUnsubscribeEvent event) {
    final String destination = 
            SimpMessageHeaderAccessor.wrap(event.getMessage()).getDestination();

    //...
}

我的问题

是否有更好的方法来监视订阅/取消订阅事件,我是否应该将其用作用户“登录”聊天室的方式,还是应该使用单独的渠道发送单独的“登录“ /”注销“消息并使用这些消息?

我认为使用订阅/取消订阅会很方便,但是显然Spring使它变得非常困难,所以我觉得必须有更好的方法。

2 个答案:

答案 0 :(得分:0)

我认为使用SessionSubscribeEvent和SessionUnsubscribeEvent是个好主意。如果您跟踪SessionID,就可以得到目的地:

private Map<String, String> destinationTracker = new HashMap<>();

@EventListener
public void handleSubscribe(final SessionSubscribeEvent event) {
    SimpMessageHeaderAccessor headers = SimpMessageHeaderAccessor.wrap(event.getMessage());
    destinationTracker.put(headers.getSessionId(), headers.getDestination());

    //...
}

@EventListener
public void handleUnsubscribe(final SessionUnsubscribeEvent event) {
    SimpMessageHeaderAccessor headers = SimpMessageHeaderAccessor.wrap(event.getMessage());
    final String destination = destinationTracker.get(headers.getSessionId());

    //...
}

答案 1 :(得分:0)

STOMP标头仅出现在与您的问题相关的框中,如此处https://stomp.github.io/stomp-specification-1.2.html#SUBSCRIBE和此处:https://stomp.github.io/stomp-specification-1.2.html#UNSUBSCRIBE

所述

只有SUBSCRIBE框架同时具有目的地和ID,UNSUBSCRIBE框架仅具有ID。 这意味着您必须记住带有目标的订阅ID,以供将来查找。必须注意,因为不同的Websocket连接通常使用/分配相同的订阅ID,因此,为了可靠地保存目标,必须在存储密钥中包含Websocket会话ID。

我编写了以下方法来获取它:

protected String getWebsocketSessionId(StompHeaderAccessor headerAccessor)
{
    // SimpMessageHeaderAccessor.SESSION_ID_HEADER seems to be set in StompSubProtocolHandler.java:261 ("headerAccessor.setSessionId(session.getId());")
    return headerAccessor.getHeader(SimpMessageHeaderAccessor.SESSION_ID_HEADER).toString();
}

StompHeaderAccessor的创建方式如下:

StompHeaderAccessor headerAccessor=StompHeaderAccessor.wrap(((SessionSubscribeEvent)event).getMessage());
StompHeaderAccessor headerAccessor=StompHeaderAccessor.wrap(((SessionUnsubscribeEvent)event).getMessage());

然后可以使用它创建唯一的订阅ID,该ID可以用作地图的键,以保存有关订阅的数据,包括目的地:

protected String getUniqueSubscriptionId(StompHeaderAccessor headerAccessor)
{
    return getWebsocketSessionId(headerAccessor)+"--"+headerAccessor.getSubscriptionId();
}

赞:

Map<String, String> destinationLookupTable=...;
// on subscribe:
destinationLookupTable.put(getUniqueSubscriptionId(headerAccessor), destination);
// on other occasions, including unsubscribe:
destination=destinationLookupTable.get(getUniqueSubscriptionId(headerAccessor));