【SpringBoot实战】WebSocket+Stomp+SockJs实现客户端状态监听


前言

对于SpringBoot而言,使用传统WebSocket监听客户端状态想必大家已经很熟悉了,比如@onClose和@onOpen注解可以用来监听客户端连线状态,但SockJs在使用上并没有可直接使用的注解或方法对客户端进行监听,这边提供了一些方法,看起来也并不复杂。

以下让我们来了解一下SpringBoot的SockJs如何来对客户端进行监听。


提示:以下是本篇文章正文内容,下面案例可供参考

一、客户端信息实体类

首先,先创建一个实体类 StompPrincipal并继承 Principal 接口 ,用于定义和保存客户端信息。

提示:这边使用到Lombok注解,详细使用方式可CSDN进行查询。

/**
 * 客户端信息实体类
 *
 * @author Tony Peng
 * @date 2022/10/28 8:45
 */
@Data
@AllArgsConstructor
public class StompPrincipal implements Principal {
    /**
     * 客户端主机名称,对应 HostName
     */
    private String name;
    /**
     * 客户端主机IP地址,对应 HostAddress
     */
    private String publicName;
}

二、自定义握手管理器

接下来,我们新建一个 CustomHandshakeHandler (自定义握手管理器)类,继承自 DefaultHandshakeHandler (默认握手管理器)。

重写 determineUser 方法,从 ServerHttpRequest 对象中可以获取到客户端信息。

这边我们获取客户端名称和客户端IP地址进行定义和保存。

/**
 * WebSocket自定义握手管理器
 *
 * @author Tony Peng
 * @date 2022/10/21 11:19
 */
public class CustomHandshakeHandler extends DefaultHandshakeHandler {

    /**
     * 重写定义用户信息方法
     *
     * @param request    握手请求对象
     * @param wsHandler  WebSocket管理器,用于管理信息
     * @param attributes 用于传递WebSocket会话的握手属性
     * @return StompPrincipal 自定义用户信息
     */
    @Override
    protected Principal determineUser(ServerHttpRequest request, WebSocketHandler wsHandler, Map<String, Object> attributes) {
    	//获取客户端主机名称
        String hostName = request.getRemoteAddress().getHostName();
        //获取客户端主机IP地址
        String hostAddress = request.getRemoteAddress().getAddress().getHostAddress();
        //StompPrincipal(name = hostName, publicName = hostAddress)
        return new StompPrincipal(hostName, hostAddress);
    }
}

这样我们继承并重写了握手管理器后,我们就可以对每个通过SockJs连接的客户端的信息进行定义和保存了。


三、WebSocket配置类

这边我们要实现 WebSocketMessageBrokerConfigurer 接口的 configureMessageBroker 方法和 registerStompEndpoints 方法。

代码如下:

/**
 * WebSocket配置类
 *
 * @author Tony Peng
 * @date 2022/10/28 11:00
 */
@Configuration
@EnableWebSocketMessageBroker
public class WebSocketMessageConfig implements WebSocketMessageBrokerConfigurer {

    /**
     * 代理配置
     *
     * @param config 配置对象
     */
    @Override
    public void configureMessageBroker(MessageBrokerRegistry config) {
        config.setApplicationDestinationPrefixes("/app");
        config.enableSimpleBroker("/topic");
    }

    /**
     * 注册STOMP协议节点并映射指定url
     *
     * @param registry 注册对象
     */
    @Override
    public void registerStompEndpoints(StompEndpointRegistry registry) {
        registry.addEndpoint("/socket").setAllowedOriginPatterns("*")
                //这边我们要设置自定义握手管理器才会生效
                .setHandshakeHandler(new CustomHandshakeHandler())
                .withSockJS();
    }
}

四、WebSocket事件监听类

我们新建一个 WebSocketEventListener 类,用来监听客户端状态事件

这边我们用到了Spring提供的一个注解 @EventListener

这个注解为我们监听客户端状态起到关键作用。

接下来我们用此注解来监听 WebSocket Session Event(WebSocket会话事件)

以下代码演示获取四个事件:

  • SessionConnectedEvent(对应WebSocket 的 @OnOpen 注解)
  • SessionDisconnectEvent (对应WebSocket 的 @OnClose 注解)
  • SessionSubscribeEvent (订阅事件会话)
  • SessionUnsubscribeEvent (取消订阅事件会话)

提示:这边使用到 @Componenet 组件扫描注解和 @Slf4j 日志注解。

//这边使用Hutool的JSON工具类进行JSON解析,详细使用方式详见 hutool.cn
import static cn.hutool.json.JSONUtil.toBean;
import static cn.hutool.json.JSONUtil.toJsonStr;

/**
 * WebSocket客户端状态监听
 *
 * @author Tony Peng
 * @date 2022/10/27 11:19
 */
@Slf4j
@Component
public class WebSocketEventListener {

    /**
     * 监听客户端连接
     *
     * @param event 连接事件对象
     */
    @EventListener
    public void handleWebSocketConnectListener(SessionConnectedEvent event) {
        StompPrincipal principal = toBean(toJsonStr(event.getUser()), StompPrincipal.class);
        log.info("WebSocket 客户端已连接: {}",
                "{ 客户端主机名: " + principal.getName() +
                        ", 客户端主机IP地址: " + principal.getPublicName() +
                        ", 会话ID: " + requireNonNull(event.getMessage().getHeaders().get("simpSessionId")) + " }");
    }

    /**
     * 监听客户端关闭事件
     *
     * @param event 关闭事件对象
     */
    @EventListener
    public void handleWebSocketCloseListener(SessionDisconnectEvent event) {
        StompPrincipal principal = toBean(toJsonStr(event.getUser()), StompPrincipal.class);
        log.info("WebSocket 客户端已关闭: {}",
                "{ 客户端主机名: " + principal.getName() +
                        ", 客户端主机IP地址: " + principal.getPublicName() +
                        ", 会话ID: " + requireNonNull(event.getMessage().getHeaders().get("simpSessionId")) + " }");
    }

    /**
     * 监听客户端订阅事件
     *
     * @param event 订阅事件对象
     */
    @EventListener
    public void handleSubscription(SessionSubscribeEvent event) {
        StompPrincipal principal = toBean(toJsonStr(event.getUser()), StompPrincipal.class);
        log.info("WebSocket 客户端已订阅: {}",
                "{ 客户端主机名: " + principal.getName() +
                        ", 客户端主机IP地址: " + principal.getPublicName() +
                        ", 订阅节点: " + requireNonNull(event.getMessage().getHeaders().get("simpDestination")) + " }");
    }

    /**
     * 监听客户端取消订阅事件
     *
     * @param event 取消订阅事件对象
     */
    @EventListener
    public void handleUnSubscription(SessionUnsubscribeEvent event) {
        StompPrincipal principal = toBean(toJsonStr(event.getUser()), StompPrincipal.class);
        log.info("WebSocket 客户端已取消订阅: {}",
                "{ 客户端主机名: " + principal.getName() +
                        ", 客户端主机IP地址: " + principal.getPublicName() +
                        ", 取消订阅节点: " + requireNonNull(event.getMessage().getHeaders().get("simpDestination")) + " }");
    }
}

来说明一下为什么客户端信息不能从 event.getUser() 直接获取客户端信息。

首先, event.getUser() 返回的是 Principal 类,这个类只有 getName() 这个方法,所以只能获取 name 字段。

在事件里,实体类映射的是 StompPrincipal 对象, 所以此时你打印 event.getUser() 时,可以看到其实是 StompPrincipal 对象,里面是有 namepublicName 这两个字段,所以这边对 even.getUser() 重新进行解析,就可以获取到 name (客户端主机名称)和 publicName (客户端主机IP地址)了。


总结

最近一直在使用Stomp + SockJs进行前后端通讯,发现没有像传统WebSocket那样有注解直接监听客户端状态,网上百度或在CSDN查询都无果,接着研究了几天关于WebSocket + Stomp + SockJs后端消息机制,可以用Spring提供的 @EventListener 来监听客户端对象,配置类也提供了拦截握手信息的 HandShakerHandler 握手管理器,自己定义一下就可以轻松获取客户端状态了,这样就可以让你的程序更加智能化,通过判断客户端状态采取各种措施。


都看到这边辣,那这篇文章一定能够帮到你,感谢你能把它看完,祝你早日成为享誉世界的 “码农”,嘻嘻。

请添加图片描述

  • 12
    点赞
  • 21
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 10
    评论
评论 10
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

愛彈吉他的小盆友

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值