环境:前台vue,后台springboot,根据用户需求指定用户,页面显示提示图标闪烁,点击图标弹出后台推送消息,消息接口采用websocket协议
实现效果:
后台:
配置项:WebSocketConfig
import org.springframework.context.annotation.Configuration;
import org.springframework.web.socket.config.annotation.EnableWebSocket;
import org.springframework.web.socket.config.annotation.WebSocketConfigurer;
import org.springframework.web.socket.config.annotation.WebSocketHandlerRegistry;
import javax.annotation.Resource;
@Configuration
@EnableWebSocket
public class WebSocketConfig implements WebSocketConfigurer {
@Resource
private MyHandler myHandler;
// 启动 websocket 线程
@Override
public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {
// System.out.println("+++++++++++");
registry.addHandler(myHandler, "/myHandler").addInterceptors(new WebSocketInterceptor()).setAllowedOrigins("*");// 接收前台url请求,这地方很重要
}
}
握手拦截器:WebSocketInterceptor
import org.springframework.http.server.ServerHttpRequest;
import org.springframework.http.server.ServerHttpResponse;
import org.springframework.http.server.ServletServerHttpRequest;
import org.springframework.web.socket.WebSocketHandler;
import org.springframework.web.socket.server.HandshakeInterceptor;
import javax.servlet.http.HttpSession;
import java.util.Map;
public class WebSocketInterceptor implements HandshakeInterceptor {
@Override
public boolean beforeHandshake(ServerHttpRequest request, ServerHttpResponse response, WebSocketHandler handler, Map<String, Object> map) throws Exception {
if (request instanceof ServletServerHttpRequest) {
ServletServerHttpRequest serverHttpRequest = (ServletServerHttpRequest) request;
HttpSession session = serverHttpRequest.getServletRequest().getSession(true);
// System.out.println("握手拦截器^^^^^^^^^^^^");
// System.out.println("username = " + serverHttpRequest.getServletRequest().getParameter("username")); // 请求参数
if (session != null) {
map.put("userId", serverHttpRequest.getServletRequest().getParameter("username")); // 设置处理器id
}
}
return true;
}
@Override
public void afterHandshake(ServerHttpRequest serverHttpRequest, ServerHttpResponse serverHttpResponse, WebSocketHandler webSocketHandler, Exception e) {
// System.out.println("握手之后^^^^^^^^^^^^");
}
}
处理器:MyHandler
import org.springframework.stereotype.Service;
import org.springframework.web.socket.CloseStatus;
import org.springframework.web.socket.TextMessage;
import org.springframework.web.socket.WebSocketMessage;
import org.springframework.web.socket.WebSocketSession;
import org.springframework.web.socket.handler.TextWebSocketHandler;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
@Service
public class MyHandler extends TextWebSocketHandler {
//在线用户列表
private static final Map<String, WebSocketSession> users;
//用户标识
private static final String CLIENT_ID = "userId";
static {
users = new HashMap<>();
}
@Override
public void afterConnectionEstablished(WebSocketSession session) throws Exception {
System.out.println("成功建立连接");
String userId = getClientId(session);
// System.out.println(userId);
if (userId != null) {
users.put(userId, session);
session.sendMessage(new TextMessage("成功建立socket连接"));
// System.out.println(userId);
// System.out.println(session);
}
}
@Override
public void handleTextMessage(WebSocketSession session, TextMessage message) {
// ...
// System.out.println(message.getPayload());
//
// WebSocketMessage message1 = new TextMessage("server:"+message);
// try {
// session.sendMessage(message1);
// } catch (IOException e) {
// e.printStackTrace();
// }
}
/**
* 发送信息给指定用户
* @param clientId
* @param message
* @return
*/
public boolean sendMessageToUser(String clientId, TextMessage message) {
if (users.get(clientId) == null) return false;
WebSocketSession session = users.get(clientId);
// System.out.println("sendMessage:" + session);
if (!session.isOpen()) return false;
try {
session.sendMessage(message);
} catch (IOException e) {
e.printStackTrace();
return false;
}
return true;
}
/**
* 广播信息
* @param message
* @return
*/
public boolean sendMessageToAllUsers(TextMessage message) {
boolean allSendSuccess = true;
Set<String> clientIds = users.keySet();
WebSocketSession session = null;
for (String clientId : clientIds) {
try {
session = users.get(clientId);
if (session.isOpen()) {
session.sendMessage(message);
}
} catch (IOException e) {
e.printStackTrace();
allSendSuccess = false;
}
}
return allSendSuccess;
}
@Override
public void handleTransportError(WebSocketSession session, Throwable exception) throws Exception {
if (session.isOpen()) {
session.close();
}
System.out.println("连接出错");
users.remove(getClientId(session));
}
@Override
public void afterConnectionClosed(WebSocketSession session, CloseStatus status) throws Exception {
System.out.println("连接已关闭:" + status);
users.remove(getClientId(session));
}
@Override
public boolean supportsPartialMessages() {
return false;
}
/**
* 获取用户标识
* @param session
* @return
*/
private String getClientId(WebSocketSession session) {
try {
String clientId = (String) session.getAttributes().get(CLIENT_ID);
return clientId;
} catch (Exception e) {
return null;
}
}
}
推送线程:PushServer
import org.apache.logging.log4j.core.config.Order;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.ApplicationArguments;
import org.springframework.boot.ApplicationRunner;
import org.springframework.stereotype.Component;
import org.springframework.web.socket.TextMessage;
import java.util.Timer;
import java.util.TimerTask;
@Component//被spring容器管理
@Order(1)//如果多个自定义ApplicationRunner,用来标明执行顺序
public class PushServer implements ApplicationRunner {
@Autowired
MyHandler handler;
@Override
public void run(ApplicationArguments applicationArguments) throws Exception{
System.out.println("-------------->" + "启动消息推送~");
TimerHandle();
}
public static void TimerHandle(){
//定时任务
Timer timer = new Timer();
timer.schedule(new TimerTask() {
@Override
public void run() {
System.out.println("------定时任务--------");
// 根据业务需求,向前台发送提示消息
}
// 每10秒钟计算一次
}, 0, 1000*10);
//阻塞队列
// Runnable run = new Runnable() {
//
// @Override
// public void run() {
// while(true){
//
// }
// }
// };
// Thread newThread = new Thread(run);
// newThread.start();
}
}
前台:
created(){
this.connectWebsocket();
},
methods: {
connectWebsocket() {
let websocket;
if (typeof WebSocket === "undefined") {
console.log("您的浏览器不支持WebSocket");
return;
} else {
if(localStorage.getItem('username') != undefined)
{
let url = 'ws://localhost:8889/myHandler?username=' + localStorage.getItem('username');
// 打开一个websocket
websocket = new WebSocket(url);
// 建立连接
websocket.onopen = () => {
// 发送数据
websocket.send("发送数据");
console.log("websocket发送数据中");
};
// 客户端接收服务端返回的数据
websocket.onmessage = evt => {
console.log("websocket返回的数据:", evt.data);
};
// 发生错误时
websocket.onerror = evt => {
console.log("websocket错误:", evt);
};
// 关闭连接
websocket.onclose = evt => {
console.log("websocket关闭:", evt);
};
}
}
}
}
PS:参考了很多博客实现这个,本想填上url,但是因为事情耽搁,时间太久了,忘记了,在此表示歉意~