当前位置: 代码迷 >> java >> 检测SessionUnsubscribeEvent的目的通道
  详细解决方案

检测SessionUnsubscribeEvent的目的通道

热度:27   发布时间:2023-07-31 10:56:15.0

我的情况

我正在构建一个小型网络聊天来了解 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 让它变得非常困难,所以我觉得必须有更好的方法。

STOMP 标头仅出现在与您的问题相关的框架中,如下所述: : 和此处: :

只有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));

我认为使用 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());

    //...
}
  相关解决方案