spring中使用stomp协议进行长连接通信

在项目当中需要和前端进行长连接,将一些实时信息给到前端。前端提出用一些比较现成的协议框架,对一些异常情况的处理要好于基础的websocket。然后就用了stomp进行长连接。

参考链接
http://www.cnblogs.com/winkey4986/p/5622758.html
http://m.blog.csdn.net/pacosonswjtu/article/details/51914567
这两个链接里的东西,一个过于简单,一个过于详细,没有一个一看就可以进行使用的demo,所以自己在学习的过程中把代码记录一下。

js的测试代码:

<!DOCTYPE html>
<html>
<head>
    <title></title>
    <meta http-equiv="Content-type" content="text/html; charset=utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge"/>
    <meta name="format-detection" content="telephone=no"/>
    <meta name="format-detection" content="email=no"/>
    <meta http-equiv="Cache-Control" content="no-cache"/>
    <meta http-equiv="Pragma" content="no-cache"/>
    <meta http-equiv="Expires" content="0"/>
    <script src="//cdn.bootcss.com/jquery/3.2.1/jquery.min.js"></script>
    <script src="//cdn.bootcss.com/sockjs-client/1.1.4/sockjs.js"></script>
    <script src="//cdn.bootcss.com/stomp.js/2.3.3/stomp.js"></script>
</head>
<body class="test">
<script>
    $(function(){
        var url = 'http://localhost:2223/teleconference/getWebSocketServer'
        var socket = new SockJS(url, undefined, {transports: ['websocket']});
        var stompClient = Stomp.over(socket);
        stompClient.connect({}, function(frame) {
            stompClient.send("/tele/init",{},"20170724");
            <!--stompClient.subscribe('/teleconference/20170724/teleStatus', function(message){-->
                <!--&lt;!&ndash;var json = JSON.parse(message.body);&ndash;&gt;-->
                <!--&lt;!&ndash;alert(message);&ndash;&gt;-->
                <!--console.log(message);-->
            <!--});-->
        });
    });
</script>
</body>
</html>

第一个部分是做长连接用的

var url = 'http://localhost:2223/teleconference/getWebSocketServer'
        var socket = new SockJS(url, undefined, {transports: ['websocket']});
        var stompClient = Stomp.over(socket);

第二个部分是测试发送普通请求的,{}中其实是填用户名密码的,这个没有就空着

stompClient.send("/tele/init",{},"20170724");

第三个部分是测试订阅模式,用来进行一对一通信的

<!--stompClient.subscribe('/teleconference/20170724/teleStatus', function(message){-->
                <!--&lt;!&ndash;var json = JSON.parse(message.body);&ndash;&gt;-->
                <!--&lt;!&ndash;alert(message);&ndash;&gt;-->
                <!--console.log(message);-->
            <!--});-->

服务端首先来个maven依赖,除了基础的依赖,还需要添加以下依赖

<dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-websocket</artifactId>
            <version>4.3.1.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-messaging</artifactId>
            <version>4.3.1.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-core</artifactId>
            <version>4.3.1.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-beans</artifactId>
            <version>4.3.1.RELEASE</version>
        </dependency>

然后是配置的连接参数,各个设置的参数用途,在注释中已经体现。

@Configuration
@EnableWebSocketMessageBroker
public class TeleWebSocketConfig extends AbstractWebSocketMessageBrokerConfigurer {
    @Override
    public void registerStompEndpoints(StompEndpointRegistry stompEndpointRegistry) {
        //webSocket连接使用url
        stompEndpointRegistry.addEndpoint("/teleconference/getWebSocketServer").withSockJS();
    }

    @Override
    public void configureMessageBroker(MessageBrokerRegistry config) {
        //向客户端发送信息时,destination的前缀必须是配置中的topic和teleconference
        config.enableSimpleBroker("/topic","/teleconference");
        //客户端向服务端发起请求时,必须以tele为前缀
        config.setApplicationDestinationPrefixes("/tele");
        //服务器向客户端发起一对一信息时,必须以teleconference为前缀
        config.setUserDestinationPrefix("/teleconference/");
    }
}

然后是一个普通的controller

@Controller
public class TeleWebSocketController {
    @Resource
    private TeleWebSocketManager teleWebSocketManager;

    @SubscribeMapping("/teleStatus")
    public String getStatus(@RequestParam String msg) {
        return msg;
    }

    @MessageMapping("/init")
    public String init(@RequestParam String msg) {
        return msg;
    }
}

最后是发送消息的service

@Service
public class TeleWebSocketManager {
    private SimpMessageSendingOperations template;

    @Autowired
    public TeleWebSocketManager(SimpMessageSendingOperations template) {
        this.template = template;
    }

    public void sendToUser(String userName,String destination,Object payload){
        template.convertAndSendToUser(userName,destination,payload);
    }

    public void send(String destination,Object payload){
        template.convertAndSend(destination,payload);
    }
}

测试一:
js中调用send方法,测试结果如下
这里写图片描述

解释:如我之前在注释里写的一样,我发送的请求是tele/init,会进入controller的init方法,将参数返回给客户端。这是普通请求,就如同http请求一样

测试二:
js调用subscribe方法,据说服务端使用@SubscribeMapping注解,在客户端订阅的时候会有返回,但是我测试下来并没有,打了断点也没有走到Controller的getStatus方法中。
即使我换个可能的写法,/{msg}/teleStatus,也没用

测试截图:
这里写图片描述

测试三:
建立连接之后,调用template.convertAndSendToUser,将信息“一对一”的发送给客户端。
这个“一对一”是用destination来区分的,本例中使用的是”/teleconference /”+ id + “teleStatus”,其中使用id来区分不同的客户端。
注意的是,你使用的destination的前缀,必须在config.enableSimpleBroker(“/topic”,”/teleconference”);这里配置过,不然客户端是收不到消息。

实际上这种方式给人的感觉并不像是传统的websocket,每个连接有session,而像是通过http请求,进行了一次消息订阅,有消息之后进行广播。所以如果有多个客户端订阅了相同的id,那么同一个消息就会被多个客户端消费。

对于那种必须要求一个客户端一个连接的,本例子是不合适的。但是我想应该spring的stomp应该支持,但是我现在没有找到相关的例子。后期如果有缘可以补上。

猜你喜欢

转载自blog.csdn.net/zzp448561636/article/details/76038915