maven依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-websocket</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
配置文件
server:
port: 80
#添加Thymeleaf配置
thymeleaf:
cache: false
prefix: classpath:/templates/
suffix: .html
mode: HTML5
encoding: UTF-8
content-type: text/html
配置文件
package com.example.admin.config;
import com.example.admin.utils.UserInterceptor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.messaging.simp.config.ChannelRegistration;
import org.springframework.messaging.simp.config.MessageBrokerRegistry;
import org.springframework.web.socket.config.annotation.AbstractWebSocketMessageBrokerConfigurer;
import org.springframework.web.socket.config.annotation.EnableWebSocketMessageBroker;
import org.springframework.web.socket.config.annotation.StompEndpointRegistry;
/**
* @Description:
registerStompEndpoints(StompEndpointRegistry registry)
configureMessageBroker(MessageBrokerRegistry config)
这个方法的作用是定义消息代理,通俗一点讲就是设置消息连接请求的各种规范信息。
registry.enableSimpleBroker("/topic")表示客户端订阅地址的前缀信息,也就是客户端接收服务端消息的地址的前缀信息(比较绕,看完整个例子,大概就能明白了)
registry.setApplicationDestinationPrefixes("/app")指服务端接收地址的前缀,意思就是说客户端给服务端发消息的地址的前缀
*/
@Configuration
@EnableWebSocketMessageBroker
public class WebSocketConfig extends AbstractWebSocketMessageBrokerConfigurer {
// 这个方法的作用是添加一个服务端点,来接收客户端的连接。
// registry.addEndpoint("/socket")表示添加了一个/socket端点,客户端就可以通过这个端点来进行连接。
// withSockJS()的作用是开启SockJS支持,
// setAllowedOrigins 跨域问题
@Override
public void registerStompEndpoints(StompEndpointRegistry registry) {
//注册两个STOMP的endpoint,分别用于广播和点对点
registry.addEndpoint("/webServer").setAllowedOrigins("*").withSockJS();
registry.addEndpoint("/queueServer").setAllowedOrigins("*").withSockJS();
}
@Override
public void configureMessageBroker(MessageBrokerRegistry registry) {
//表示客户端订阅地址的前缀信息,也就是客户端接收服务端消息的地址的前缀信息,topic用来广播,user用来实现p2p
registry.enableSimpleBroker("/topic", "/user");
//指服务端接收地址的前缀,意思就是说客户端给服务端发消息的地址的前缀
registry.setApplicationDestinationPrefixes("/app");
//registry.setUserDestinationPrefix("/user");这句话表示给指定用户发送一对一的主题前缀是"/user"。
}
/**
* 配置客户端入站通道拦截器
*/
@Override
public void configureClientInboundChannel(ChannelRegistration registration) {
registration.interceptors(userInterceptor());
}
/**
* @return
* @Title: createUserInterceptor
* @Description: 将客户端渠道拦截器加入spring ioc容器
*/
@Bean
public UserInterceptor userInterceptor() {
return new UserInterceptor();
}
}
获取用户信息
package com.example.admin.utils;
import com.example.admin.model.User;
import org.springframework.messaging.Message;
import org.springframework.messaging.MessageChannel;
import org.springframework.messaging.simp.SimpMessageHeaderAccessor;
import org.springframework.messaging.simp.stomp.StompCommand;
import org.springframework.messaging.simp.stomp.StompHeaderAccessor;
import org.springframework.messaging.support.ChannelInterceptorAdapter;
import org.springframework.messaging.support.MessageHeaderAccessor;
import java.util.LinkedList;
import java.util.Map;
public class UserInterceptor extends ChannelInterceptorAdapter {
/**
* 获取包含在stomp中的用户信息
*/
@SuppressWarnings("rawtypes")
@Override
public Message<?> preSend(Message<?> message, MessageChannel channel) {
StompHeaderAccessor accessor = MessageHeaderAccessor.getAccessor(message, StompHeaderAccessor.class);
if (StompCommand.CONNECT.equals(accessor.getCommand())) {
Object raw = message.getHeaders().get(SimpMessageHeaderAccessor.NATIVE_HEADERS);
if (raw instanceof Map) {
Object name = ((Map) raw).get("name");
if (name instanceof LinkedList) {
// 设置当前访问器的认证用户
accessor.setUser(new User(((LinkedList) name).get(0).toString()));
}
}
}
return message;
}
}
控制器
package com.example.admin.web;
import com.example.admin.model.ReceiveMessage;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.messaging.handler.annotation.MessageMapping;
import org.springframework.messaging.simp.SimpMessagingTemplate;
import org.springframework.messaging.simp.user.SimpUser;
import org.springframework.messaging.simp.user.SimpUserRegistry;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
@Controller
@RequestMapping("/restful")
public class RestFulController {
@Autowired
public SimpMessagingTemplate template;
@Autowired
private SimpUserRegistry userRegistry;
/**
* @Description:广播
* @Author:hui.yunfei@qq.com
* @Date: 2019/6/4
*/
@MessageMapping("/subscribe")
//@SendTo
public void subscribe(ReceiveMessage rm) {
System.out.println("服务端接收到广播消息:"+rm);
for(int i =1;i<=5;i++) {
//广播使用convertAndSend方法,第一个参数为目的地,和js中订阅的目的地要一致
template.convertAndSend("/topic/getResponse", rm.getMsg());
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
/**
* @Description:点对点
* @Author:hui.yunfei@qq.com
* @Date: 2019/6/4
*/
@MessageMapping("/queue")
//@SendToUser
public void queue(ReceiveMessage rm) {
System.out.println("服务端接收到点对点消息:"+rm);
for(int i =1;i<=5;i++) {
/*广播使用convertAndSendToUser方法,第一个参数为用户id,此时js中的订阅地址为
"/user/" + 用户Id + "/message",其中"/user"是固定的*/
template.convertAndSendToUser(rm.getUser(),"/message",rm.getMsg());
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
/**
* @Description:主动推消息到客户端(可配做定时器)
* @Author:hui.yunfei@qq.com
* @Date: 2019/6/4
*/
@RequestMapping("/send")
public void send(){
template.convertAndSend("/topic/getResponse", "我是服务器主动推送的消息");
}
@RequestMapping("/getOnline")
@ResponseBody
public void getOnline(){
System.out.println("当前在线人数:" + userRegistry.getUserCount());
int i = 1;
for (SimpUser user : userRegistry.getUsers()) {
System.out.println("用户" + i++ + "---" + user);
}
}
}
页面
<html>
<head>
<meta charset="UTF-8">
<title>Hello topic</title>
<script src="sockjs.min.js"></script>
<script src="stomp.min.js"></script>
<script src="jquery-3.2.1.min.js"></script>
<script type="text/javascript">
var stompClient = null;
function setConnected(connected){
document.getElementById("connect").disabled = connected;
document.getElementById("disconnect").disabled = !connected;
$("#response").html();
}
function connect() {
var socket = new SockJS("/webServer");
stompClient = Stomp.over(socket);
stompClient.connect({name:'yunfei'}, function(frame) {
setConnected(true);
console.log('Connected: ' + frame);
stompClient.subscribe('/topic/getResponse', function(response){
var response1 = document.getElementById('response');
var p = document.createElement('p');
p.style.wordWrap = 'break-word';
p.appendChild(document.createTextNode(response.body));
response1.appendChild(p);
});
});
}
function disconnect() {
if (stompClient != null) {
stompClient.disconnect();
}
setConnected(false);
console.log("Disconnected");
}
function sendName() {
var name = document.getElementById('name').value;
console.info(1111111111);
stompClient.send("/app/subscribe", {}, JSON.stringify({ 'msg': name }));
}
</script>
</head>
<body onload="disconnect()">
<noscript><h2 style="color: #ff0000">Seems your browser doesn't support Javascript! Websocket relies on Javascript being enabled. Please enable
Javascript and reload this page!</h2></noscript>
<div>
<div>
<button id="connect" onclick="connect();">Connect</button>
<button id="disconnect" disabled="disabled" onclick="disconnect();">Disconnect</button>
</div>
<div id="conversationDiv">
<labal>名字</labal><input type="text" id="name" />
<button id="sendName" onclick="sendName();">Send</button>
<p id="response"></p>
</div>
</div>
</body>
</html>
<html>
<head>
<meta charset="UTF-8">
<title>Hello queue</title>
<script src="sockjs.min.js"></script>
<script src="stomp.min.js"></script>
<script src="jquery-3.2.1.min.js"></script>
<script type="text/javascript">
var stompClient = null;
function setConnected(connected){
document.getElementById("connect").disabled = connected;
document.getElementById("disconnect").disabled = !connected;
$("#response").html();
}
function connect() {
var socket = new SockJS("/queueServer");
stompClient = Stomp.over(socket);
stompClient.connect({}, function(frame) {
setConnected(true);
console.log('Connected: ' + frame);
stompClient.subscribe('/user/'+document.getElementById('user').value+'/message', function(response){
var response1 = document.getElementById('response');
var p = document.createElement('p');
p.style.wordWrap = 'break-word';
p.appendChild(document.createTextNode(response.body));
response1.appendChild(p);
});
});
}
function disconnect() {
if (stompClient != null) {
stompClient.disconnect();
}
setConnected(false);
console.log("Disconnected");
}
function sendName() {
var name = document.getElementById('name').value;
var user = document.getElementById('user').value;
console.info(1111111111);
stompClient.send("/app/queue", {}, JSON.stringify({ 'msg': name,'user':user}));
}
</script>
</head>
<body onload="disconnect()">
<noscript><h2 style="color: #ff0000">Seems your browser doesn't support Javascript! Websocket relies on Javascript being enabled. Please enable
Javascript and reload this page!</h2></noscript>
<div>
<div>
<labal>用户</labal><input type="text" id="user" />
<button id="connect" onclick="connect();">Connect</button>
<button id="disconnect" disabled="disabled" onclick="disconnect();">Disconnect</button>
</div>
<div id="conversationDiv">
<labal>名字</labal><input type="text" id="name" />
<button id="sendName" onclick="sendName();">Send</button>
<p id="response"></p>
</div>
</div>
</body>
</html>