Spring Boot 的 WebSocket 开发说明文档

SpringBoot 框架添加 WebSocket 支持

环境要求和关键技术

开发关键步骤

在 Spring Boot 项目中添加 websocket 支持

//gradle 添加方式
compile('org.springframework.boot:spring-boot-starter-websocket')
<!-- maven 添加方式 -->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-websocket</artifactId>
</dependency>

在项目中添加 websocket 的 Spring 配置

  1. 向SpringBoog注册websocket服务。
  2. 连接前使用握手拦截器处理用户认证信息(非必要)。
  3. 设置通信规范(非必要)。
@Configuration  //注册为 Spring 配置类
/*
 * 开启使用STOMP协议来传输基于代理(message broker)的消息
 * 启用后控制器支持@MessgeMapping注解
 */
@EnableWebSocketMessageBroker
//继承 AbstractWebSocketMessageBrokerConfigurer 的配置类实现 WebSocket 配置
public class WebSocketConfig extends AbstractWebSocketMessageBrokerConfigurer {

    //注册STOMP协议节点并映射url
    @Override
    public void registerStompEndpoints(StompEndpointRegistry registry) {
        registry.addEndpoint("/websocket") //注册一个 /websocket 的 websocket 节点
                .addInterceptors(myHandshakeInterceptor())  //添加 websocket握手拦截器
                .setHandshakeHandler(myDefaultHandshakeHandler())   //添加 websocket握手处理器
                .setAllowedOrigins("*") //设置允许可跨域的域名
                .withSockJS();  //指定使用SockJS协议
    }

    /**
     * WebSocket 握手拦截器
     * 可做一些用户认证拦截处理
     */
    private HandshakeInterceptor myHandshakeInterceptor(){
        return new HandshakeInterceptor() {
            /**
             * websocket握手连接
             * @return 返回是否同意握手
             */
            @Override
            public boolean beforeHandshake(ServerHttpRequest request, ServerHttpResponse response, WebSocketHandler wsHandler, Map<String, Object> attributes) throws Exception {
                ServletServerHttpRequest req = (ServletServerHttpRequest) request;
                //通过url的query参数获取认证参数
                String token = req.getServletRequest().getParameter("token");
                //根据token认证用户,不通过返回拒绝握手
                Principal user = authenticate(token);
                if(user == null){
                    return false;
                }
                //保存认证用户
                attributes.put("user", user);
                return true;
            }

            @Override
            public void afterHandshake(ServerHttpRequest request, ServerHttpResponse response, WebSocketHandler wsHandler, Exception exception) {

            }
        };
    }

    //WebSocket 握手处理器
    private DefaultHandshakeHandler myDefaultHandshakeHandler(){
        return new DefaultHandshakeHandler(){
            @Override
            protected Principal determineUser(ServerHttpRequest request, WebSocketHandler wsHandler, Map<String, Object> attributes) {
                //设置认证通过的用户到当前会话中
                return (Principal)attributes.get("user");
            }
        };
    }

    /**
     * 定义一些消息连接规范(也可不设置)
     * @param registry
     */
    @Override
    public void configureMessageBroker(MessageBrokerRegistry registry) {
        //设置客户端接收消息地址的前缀(可不设置)
        registry.enableSimpleBroker(
                "/topic",   //广播消息前缀
                "/queue"    //点对点消息前缀
        );
        //设置客户端接收点对点消息地址的前缀,默认为 /user
        registry.setUserDestinationPrefix("/user");
        //设置客户端向服务器发送消息的地址前缀(可不设置)
        registry.setApplicationDestinationPrefixes("/app");
    }

    /**
     * 根据token认证授权
     * @param token
     */
    private Principal authenticate(String token){
        //TODO 实现用户的认证并返回用户信息,如果认证失败返回 null
        //用户信息需继承 Principal 并实现 getName() 方法,返回全局唯一值
        return null;
    }
}

WebSocket的 消息处理

  1. 使用 @MessageMapping 接收客户端消息。
  2. 使用 @SendTo 发送广播消息。
  3. 使用 @SendToUser 反馈客户端发送消息结果。
  4. 使用 SimpMessagingTemplate 消息模板发送广播消息和给指定客户端发消息。
@Controller //注册一个Controller,WebSocket的消息处理需要放在Controller下
public class WsController {

    @Autowired
    private SimpMessagingTemplate messagingTemplate;    //Spring WebSocket消息发送模板

    //发送广播通知
    @MessageMapping("/addNotice")   //接收客户端发来的消息,客户端发送消息地址为:/app/addNotice
    @SendTo("/topic/notice")        //向客户端发送广播消息(方式一),客户端订阅消息地址为:/topic/notice
    public WsMessage notice(String notice, Principal fromUser) {
        //TODO 业务处理
        WsMessage msg = new WsMessage();
        msg.setFromName(fromUser.getName());
        msg.setContent(notice);

        //向客户端发送广播消息(方式二),客户端订阅消息地址为:/topic/notice
//        messagingTemplate.convertAndSend("/topic/notice", msg);
        return msg;
    }

    //发送点对点消息
    @MessageMapping("/msg")         //接收客户端发来的消息,客户端发送消息地址为:/app/msg
    @SendToUser("/queue/msg/result") //向当前发消息客户端(就是自己)发送消息的发送结果,客户端订阅消息地址为:/user/queue/msg/result
    public boolean sendMsg(WsMessage message, Principal fromUser){
        //TODO 业务处理
        message.setFromName(fromUser.getName());

        //向指定客户端发送消息,第一个参数Principal.name为前面websocket握手认证通过的用户name(全局唯一的),客户端订阅消息地址为:/user/queue/msg/new
        messagingTemplate.convertAndSendToUser(message.getToName(), "/queue/msg/new", message);
        return true;
    }
}

消息处理对象

public class WsMessage {
    //消息接收人,对应认证用户Principal.name(全局唯一)
    private String toName;
    //消息发送人,对应认证用户Principal.name(全局唯一)
    private String fromName;
    //消息内容
    private Object content;

    public String getToName() { return toName; }
    public void setToName(String toName) { this.toName = toName; }
    public String getFromName() { return fromName; }
    public void setFromName(String fromName) { this.fromName = fromName; }
    public Object getContent() { return content; }
    public void setContent(Object content) { this.content = content; }
}

本案例各个通信地址

通信地址除了握手请求地址最好写完整的地址,其它地址均不用写域名或IP
- 握手请求(connect):http://域名或IP/websocket?token=认证token
- 广播订阅地址(subscribe):/topic/notice
- 个人消息订阅地址(subscribe):/user/queue/msg/new
- 发送广播通知(send):/app/addNotice
- 发送点对点消息(send):/app/msg
- 获取消息发送结果(subscribe):/user/queue/msg/result

js 连接 WebSocket

依赖
var sock = new SockJS(contextPath+'websocket?token=' + token);  //连接节点
var stomp = Stomp.over(sock);
stomp.connect({}, function(frame){  //连接成功后订阅消息接口
    //订阅个人消息
    stomp.subscribe('/user/queue/msg/new', function(response){
        var result = response.body;
        //TODO something
    });
    //消息发送结果
    stomp.subscribe('/user/queue/msg/result', function(response){});

    //订阅广播消息
    stomp.subscribe('/topic/notice', function(response){});
});

//发送广播
stomp.send('/app/addNotice', {}, '广播内容');
//发送消息
var msg = {
    toName: '接收人',
    content: '消息内容'
};
stomp.send('/app/msg', {}, JSON.stringify(msg));
评论 8
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值