在项目当中需要和前端进行长连接,将一些实时信息给到前端。前端提出用一些比较现成的协议框架,对一些异常情况的处理要好于基础的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){-->
<!--<!–var json = JSON.parse(message.body);–>-->
<!--<!–alert(message);–>-->
<!--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){-->
<!--<!–var json = JSON.parse(message.body);–>-->
<!--<!–alert(message);–>-->
<!--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应该支持,但是我现在没有找到相关的例子。后期如果有缘可以补上。