简体   繁体   中英

Detect destination channel of SessionUnsubscribeEvent

My Situation

I'm building a small web chat to learn about Spring and Spring WebSocket. You can create different rooms, and each room has it's own channel at /topic/room/{id} .

My goal is to detect when users join and leave a chat room and I thought I could use Spring WebSocket's SessionSubscribeEvent and SessionUnsubscribeEvent for this.

Getting the Destination from the SessionSubscribeEvent is trivial:

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

    //...
}

However, the SessionUnsubscribeEvent does not seem to carry the destination channel, destination is null in the following snippet:

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

    //...
}

My Question

Is there a better way to watch for subscribe/unsubscribe events and should I even be using those as a way for a user to "log in" to a chat room, or should I rather use a separate channel to send separate "log in"/"log out" messages and work with those?

I thought using subscribe/unsubscribe would've been very convenient, but apparently Spring makes it very hard, so I feel like there has to be a better way.

STOMP Headers only appear in the frames relevant to your question as described here: https://stomp.github.io/stomp-specification-1.2.html#SUBSCRIBE and here: https://stomp.github.io/stomp-specification-1.2.html#UNSUBSCRIBE

Only the SUBSCRIBE frame has both destination and id, the UNSUBSCRIBE frame has only an id. This means you have to remember the subscription id with the destination for future lookup. Care must be taken because different Websocket connections usually use/assign the same subscription ids, so to save destinations reliably, you have to include the websocket session id in your storage key.

I wrote the following method to get it:

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 is created like this:

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

This can then be used to create a unique subscription id which can be used as a key for a map to save data about the subscription, including the destination:

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

Like this:

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

I think using SessionSubscribeEvent and SessionUnsubscribeEvent is a good idea for that matter. You can get the destination if you keep track of the 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());

    //...
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM