spring websocket 使用@SendToUser

8 篇文章 0 订阅
8 篇文章 0 订阅

之前我们利用@SendTo在方法上进行注解,方法的返回值会被messageconverter转化并推送到消息代理器中,由消息代理器广播到订阅路径去

@MessageMapping("bar")  //@MessageMapping接收客户端消息
@SendTo("/topic/brocast") //@SendTo广播消息出去
public String handle1(String msg) {
    return msg;
}
 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 1
  • 2
  • 3
  • 4
  • 5

上面msg会被广播到”/topic/brocast”这个订阅路径中,只要客户端订阅了这条路径,不管是哪个用户,都会接收到消息

那么需求来了,如果我只是想简单的用websocket向服务器请求资源而已,然后服务器你就把资源给我就行了,别的用户就不用你广播推送了,简单点,就是我请求,你就推送给我。

spring websocket 可以使用@SendToUser做到这一点,在使用@SendToUser之前,我们需要明白以下几点:

1.spring webscoket通道的建立最开始还是源于http协议的第一次握手,握手成功之后,就打开了浏览器和服务器的webscoket通过,这时,httprequest中的登录授权信息即javax.security.Principal会被绑定到websocket的session中

2.spring webscoket能识别带”/user”的订阅路径并做出处理,例如,如果浏览器客户端,订阅了’/user/topic/greetings’这条路径,

stompClient.subscribe('/user/topic/greetings', function(data) {
    //...
});
 
 
  • 1
  • 2
  • 3
  • 1
  • 2
  • 3

就会被spring websocket利用UserDestinationMessageHandler进行转化成”/topic/greetings-usererbgz2rq”,”usererbgz2rq”中,user是关键字,erbgz2rq是sessionid,这样子就把用户和订阅路径唯一的匹配起来了

3.spring webscoket在使用@SendToUser广播消息的时候,

@MessageMapping("handle") 
@SendToUser("/topic/greetings")
public String handle(String msg) {
    //...
    return msg;
}
 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

“/topic/greetings”会被UserDestinationMessageHandler转化成”/user/role1/topic/greetings”,role1是用户的登录帐号,这样子就把消息唯一的推送到请求者的订阅路径中去,这时候,如果一个帐号打开了多个浏览器窗口,也就是打开了多个websocket session通道,这时,spring webscoket默认会把消息推送到同一个帐号不同的session,你可以利用broadcast = false把避免推送到所有的session中

@MessageMapping("handle") 
@SendToUser(value = "/topic/greetings",broadcast = false)
public String handle(String msg) {
    //...
    return name;
}
 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

下面来做个demo,首先是服务器要配置登录验证权限,这里利用tomcat的basic安全验证,在web.xml里面配置

<security-constraint>
<web-resource-collection>
        <web-resource-name>protect resources </web-resource-name>
        <url-pattern>/*</url-pattern>
        <http-method>HEAD</http-method>
        <http-method>GET</http-method>
        <http-method>POST</http-method>
        <http-method>PUT</http-method>
        <http-method>DELETE</http-method>
    </web-resource-collection>
    <auth-constraint>
        <role-name>role1</role-name>
    </auth-constraint>
    <user-data-constraint>
        <transport-guarantee>NONE</transport-guarantee>
    </user-data-constraint>
</security-constraint>
<login-config>
    <auth-method>BASIC</auth-method>
</login-config>
<security-role>
    <description>Role1</description>
    <role-name>role1</role-name>
</security-role> 
 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24

role1是登录的角色名,其中验证信息可以在tomcat-users.xml里面配置

 <role rolename="tomcat"/>
 <role rolename="role1"/>
 <user username="tomcat" password="tomcat" roles="tomcat"/>
 <user username="both" password="tomcat" roles="tomcat,role1"/>
 <user username="role1" password="tomcat" roles="role1"/>
 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 1
  • 2
  • 3
  • 4
  • 5

这里有俩个帐号,role1和both,角色都有role1,password都是tomcat,我们可以利用这俩个帐号在IE9和谷歌浏览器进行登录

这里写图片描述

然后服务器,我们分别使用@SendTo和@SendToUser进行广播推送和精准推送

首先我们注册下spring webscoket服务器

@Configuration
@EnableWebSocketMessageBroker
public class WebSocketMessageBrokerConfig implements WebSocketMessageBrokerConfigurer {

    @Override
    public void registerStompEndpoints(StompEndpointRegistry registry) {

        //portfolio-stomp就是websocket的端点,客户端需要注册这个端点进行链接,withSockJS允许客户端利用sockjs进行浏览器兼容性处理
        registry.addEndpoint("/portfolio-stomp").withSockJS(); 

    }

    @Override
    public void configureMessageBroker(MessageBrokerRegistry registry) {
        registry.enableSimpleBroker("/topic");               //设置服务器广播消息的基础路径
        registry.setApplicationDestinationPrefixes("/app");  //设置客户端订阅消息的基础路径
        registry.setPathMatcher(new AntPathMatcher("."));    //可以已“.”来分割路径,看看类级别的@messageMapping和方法级别的@messageMapping
    }

    @Override
    public boolean configureMessageConverters(List<MessageConverter> messageConverters) {

        return true;
    }

    @Override
    public void configureWebSocketTransport(WebSocketTransportRegistration registry) {
        // TODO Auto-generated method stub
        registry.addDecoratorFactory(new MyWebSocketHandlerDecoratorFactory());
    }

    @Override
    public void configureClientInboundChannel(ChannelRegistration registration) {
         registration.setInterceptors(new MyChannelInterceptor());
    }

    @Override
    public void configureClientOutboundChannel(ChannelRegistration registration) {
        // TODO Auto-generated method stub

    }

    @Override
    public void addArgumentResolvers(List<HandlerMethodArgumentResolver> argumentResolvers) {
        // TODO Auto-generated method stub

    }

    @Override
    public void addReturnValueHandlers(List<HandlerMethodReturnValueHandler> returnValueHandlers) {
        // TODO Auto-generated method stub

    }

}

 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56

然后写下服务的的接收和发送

@Controller
@RequestMapping("/webSocket")
@MessageMapping("foo")
public class WebSocketController {

    /**
     * 精准推送
     * @param msg
     * @param principal
     * @return
     */
    @MessageMapping("handle1") 
    @SendToUser(value = "/topic/greetings1",broadcast = false)
    public String handle1(String msg,Principal principal) {

        return "精准推送,只推送到" + principal.getName();
    }


    /**
     * 广播推送
     * @param msg
     * @param principal
     * @return
     */
    @MessageMapping("handle2") 
    @SendTo("topic/greetings2")
    public String handle2(String msg,Principal principal) {

        return "广播推送,所有用户都收得到";
    }   
}
 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32

最后在浏览器客户端,我们利用sockjs和stomp.js链接并发送和订阅消息,其中在websocket.js代码就是这样子的

var socket = new SockJS('/whats/portfolio-stomp');
var stompClient = Stomp.over(socket);
stompClient.connect({}, function(frame) {

stompClient.subscribe('/user/topic/greetings1', function(data) {
         $("#ret").text(data.body);
    });

    stompClient.subscribe('/topic/greetings2', function(data) {
        $("#ret").text(data.body);
    });
});

/**
 * 精准推送
 */
document.getElementById("ws1").onclick = function() {
    stompClient.send("/app/foo.handle2",{},{
        msg : "精准推送"
    });
}

/**
 * 广播推送
 */
document.getElementById("ws").onclick = function() {

    stompClient.send("/app/foo.handle1",{},{
        msg : "广播推送"
    });

}

 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33

jsp页面其实就是这样子的

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<!DOCTYPE html >
<html>
<head>
<meta charset="UTF-8">
<title>websocket</title>

</head>
<body> 

<h1>hello websocket client !!</h1>

<button id = "ws">精准推送</button>
<button id = "ws1">广播推送</button>

<span id ="ret"></span>

<script type="text/javascript" src="${pageContext.request.contextPath}/content/uilib/websocket/sockjs-1.0.3.js"></script>
<script type="text/javascript" src="${pageContext.request.contextPath}/content/uilib/websocket/stomp.js"></script>
<script type="text/javascript" src="${pageContext.request.contextPath}/content/js/websocket/websocket.js"></script>
</body>
</html>
 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23

到这里就完成了功能了

精准推送

这里写图片描述

广播推送

这里写图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值