1、导入依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-websocket</artifactId>
</dependency>
2、配置
@Configuration
public class WebSocketConfig {
@Bean
public ServerEndpointExporter serverEndpointExporter(){
return new ServerEndpointExporter();
}
@Autowired
private RequestListener requestListener;
@Bean
public ServletListenerRegistrationBean<RequestListener> servletListenerRegistrationBean() {
ServletListenerRegistrationBean<RequestListener> servletListenerRegistrationBean = new ServletListenerRegistrationBean<>();
servletListenerRegistrationBean.setListener(requestListener);
return servletListenerRegistrationBean;
}
}
@Controller
@ServerEndpoint(value = "/websocket", configurator = HttpSessionConfigurator.class)
@Slf4j
public class ScocketHandler {
/*websocket 客户端会话 通过Session 向客户端发送数据*/
private Session session;
/*存活的session集合(使用线程安全的map保存)*/
private static Map<Long, ScocketHandler> sessionManger = new ConcurrentHashMap<Long, ScocketHandler>();
/*websocket 连接建立成功后进行调用*/
@OnOpen
public void onOpen(Session session, EndpointConfig config) {
this.session = session;
HttpSession httpSession = (HttpSession) config.getUserProperties().get(HttpSession.class.getName());
Long userId = (Long) httpSession.getAttribute("userId");
sessionManger.put(userId, this);
System.out.println("websocket有新的链接" + sessionManger.size());
}
/*websocket 链接关闭调用的方法*/
@OnClose
public void onClose() {
sessionManger.remove(this);
}
/*收到客户端消息后调用的方法*/
@OnMessage
public void onMessage(String message) throws IOException {
}
/*websocket 发生错误时进行调用*/
@OnError
public void onError(Session session, Throwable error) {
error.printStackTrace();
}
/**
* @Description: 推送给所有的用户
* @Param: [message]
* @return: void
* @Author: cherish
* @Date: 2019/9/13
*/
public static void sendAll(String message) throws IOException {
for (Map.Entry<Long, ScocketHandler> entry : sessionManger.entrySet()) {
Session session = entry.getValue().session;
session.getBasicRemote().sendText("" + message);
}
}
/**
* @Description: 发送信息给指定ID用户,如果用户不在线则返回不在线信息给自己
* @Param: [message, toUserId]
* @return: void
* @Author: cherish
* @Date: 2019/9/13
*/
public static void sendtoUser(TransResult result, Long toUserId) throws IOException {
Session session = sessionManger.get(toUserId).session;
if (session != null) {
ObjectMapper mapper = new ObjectMapper();
session.getBasicRemote().sendText(mapper.writeValueAsString(result));
} else {
log.info("用户不在线,推送到用户的代办导出任务");
}
}
public Session getSession() {
return session;
}
public void setSession(Session session) {
this.session = session;
}
}
@Component
public class RequestListener implements ServletRequestListener {
@Override
public void requestInitialized(ServletRequestEvent sre) {
//将所有request请求都携带上httpSession
((HttpServletRequest) sre.getServletRequest()).getSession();
}
public RequestListener() {
}
@Override
public void requestDestroyed(ServletRequestEvent arg0) {
}
}
public class HttpSessionConfigurator extends ServerEndpointConfig.Configurator {
/* 修改握手,就是在握手协议建立之前修改其中携带的内容 */
@Override
public void modifyHandshake(ServerEndpointConfig sec, HandshakeRequest request, HandshakeResponse response) {
HttpSession httpSession = (HttpSession) request.getHttpSession();
sec.getUserProperties().put(HttpSession.class.getName(), httpSession);
super.modifyHandshake(sec, request, response);
}
}
@Controller
@RequestMapping("/smart/etl")
public class TestSocketController {
//页面请求
@GetMapping("/socket/{cid}")
public ModelAndView socket(@PathVariable String cid,HttpServletRequest request) {
ModelAndView mav = new ModelAndView("/socket");
HttpSession session = request.getSession();
session.setAttribute("userId",1L);
mav.addObject("cid", cid);
return mav;
}
//推送数据接口
@ResponseBody
@RequestMapping("/socket/push/{cid}")
public Object pushToWeb(@PathVariable Long cid, String message, HttpServletRequest request) {
try {
TransResult transResult = new TransResult();
transResult.setMessage("您好呀");
//启动线程实现消息发送
new Thread(new Runnable() {
@Override
public void run() {
System.out.println("开始大文件导入");
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("导入结束");
// 发送通知
try {
ScocketHandler.sendtoUser(transResult, 1L);
} catch (IOException e) {
e.printStackTrace();
}
}
}).start();
} catch (Exception e) {
e.printStackTrace();
}
return cid;
}
@ResponseBody
@RequestMapping("/socket/sendAll/{cid}")
public Object sendAll(@PathVariable Long cid, String message, HttpServletRequest request) {
try {
ScocketHandler.sendAll("您好呀");
} catch (IOException e) {
e.printStackTrace();
}
return cid;
}
}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8"></meta>
<title>Title</title>
</head>
<body>
hello world!
</body>
<script>
var socket;
if(typeof(WebSocket) == "undefined") {
console.log("您的浏览器不支持WebSocket");
}else{
console.log("您的浏览器支持WebSocket");
//实现化WebSocket对象,指定要连接的服务器地址与端口 建立连接
//等同于
index = new WebSocket("ws://localhost:9999/websocket");
//socket = new WebSocket("${basePath}websocket/${cid}".replace("http","ws"));
//打开事件
index.onopen = function() {
console.log("Socket 已打开");
//socket.send("这是来自客户端的消息" + location.href + new Date());
};
//获得消息事件
index.onmessage = function(msg) {
// alert("dd")
console.log(msg.data);
//发现消息进入 开始处理前端触发逻辑
};
//关闭事件
index.onclose = function() {
console.log("Socket已关闭");
};
//发生了错误事件
index.onerror = function() {
alert("Socket发生了错误");
//此时可以尝试刷新页面
}
//离开页面时,关闭socket
//jquery1.8中已经被废弃,3.0中已经移除
// $(window).unload(function(){
// socket.close();
//});
}
</script>
</html>
通过
private static Map<Long, ScocketHandler> sessionManger = new ConcurrentHashMap<Long, ScocketHandler>();
维护用户与session的对应关系,在websocket中可以获取到HttpSession中的用户,从而将信息发送给指定的用户,从而实现推送,实现获取session则需要配置如上所述的RequestListener和HttpSessionConfiguratitor及使用
ServletListenerRegistrationBean注册该监听器。