引入spring websocket 和springboot 启动 pom.xml:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
<dependency>
<groupId>com.huasuhui</groupId>
<artifactId>huasuhui-spring-boot-starter-dubbo</artifactId>
<version>1.0.0</version>
</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>
加入websocket 配置文件:
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.socket.server.standard.ServerEndpointExporter;
/**
* WebScoket配置处理器
*/
@Configuration
public class WebSocketConfig {
/**
* ServerEndpointExporter 作用
* 这个Bean会自动注册使用@ServerEndpoint注解声明的websocket endpoint
* @return
*/
@Bean
public ServerEndpointExporter serverEndpointExporter() {
return new ServerEndpointExporter();
}
}
编写websocket 服务 WebSocketServer :
package com.huash.websocket.service;
import java.io.IOException;
import java.util.Date;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicInteger;
import javax.websocket.OnClose;
import javax.websocket.OnError;
import javax.websocket.OnMessage;
import javax.websocket.OnOpen;
import javax.websocket.Session;
import javax.websocket.server.PathParam;
import javax.websocket.server.ServerEndpoint;
import org.apache.commons.lang.StringUtils;
import org.springframework.stereotype.Component;
import com.alibaba.fastjson.JSON;
import com.huash.websocket.listener.Message;
@ServerEndpoint("/webSocket/{username}")
@Component
public class WebSocketServer {
//静态变量,用来记录当前在线连接数。应该把它设计成线程安全的。
private static AtomicInteger onlineNum = new AtomicInteger();
//concurrent包的线程安全Set,用来存放每个客户端对应的WebSocketServer对象。
private static ConcurrentHashMap<String, Session> sessionPools = new ConcurrentHashMap<>();
//发送消息
public void sendMessage(Session session, String message) throws IOException {
if(session != null){
synchronized (session) {
if (session.isOpen()) {
//System.out.println("发送数据:" + message);
session.getBasicRemote().sendText(message);
}
}
}
}
//给指定用户发送信息
public void sendInfo(String userName, String message){
if (sessionPools.size() > 0 && !StringUtils.isBlank(message)){
Session session = sessionPools.get(userName);
if (session != null && session.isOpen()){
try {
sendMessage(session, message);
}catch (Exception e){
e.printStackTrace();
}
}
}
}
// 群发消息
public void broadcast(String message){
if (sessionPools.size() > 0 && !StringUtils.isBlank(message)) {
for (Session session: sessionPools.values()) {
try {
if (session != null && session.isOpen()){
sendMessage(session,message);
//session.getBasicRemote().sendText(message);
}
else {
session.close();
}
} catch(Exception e){
e.printStackTrace();
System.out.println("发生错误....");
continue;
}
}
}
}
//建立连接成功调用
@OnOpen
public void onOpen(Session session, @PathParam(value = "username") String userName){
// 将老的session关闭
try {
Session session2 = sessionPools.get(userName);
if (session2 != null) {
session2.close();
}
} catch (IOException e) {
}
// 用户加入连接
sessionPools.put(userName, session);
addOnlineCount();
}
//关闭连接时调用
@OnClose
public void onClose(@PathParam(value = "username") String userName){
// 将老的seesion关闭
try {
Session session2 = sessionPools.get(userName);
if (session2 != null) {
session2.close();
}
} catch (Exception e) {
}
// 去掉用户连接
sessionPools.remove(userName);
subOnlineCount();
}
//收到客户端信息后,根据接收人的username把消息推下去或者群发
// to=-1群发消息
@OnMessage
public void onMessage(String message) throws IOException{
//System.out.println("server get" + message);
Message msg=JSON.parseObject(message, Message.class);
msg.setDate(new Date());
if (msg.getTo().equals("-1")) {
broadcast(JSON.toJSONString(msg,true));
} else {
sendInfo(msg.getTo(), JSON.toJSONString(msg,true));
}
}
//错误时调用
@OnError
public void onError(Session session, Throwable throwable){
//System.out.println("发生错误");
//throwable.printStackTrace();
}
public static void addOnlineCount(){
onlineNum.incrementAndGet();
}
public static void subOnlineCount() {
onlineNum.decrementAndGet();
}
public static AtomicInteger getOnlineNumber() {
return onlineNum;
}
public static ConcurrentHashMap<String, Session> getSessionPools() {
return sessionPools;
}
}
编写一个context类 ,方便后面服务端主动向前端推送消息:
import java.util.HashMap;
import java.util.Map;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.stereotype.Component;
@Component
public class HuashContext implements ApplicationContextAware{
//系统事件///
public static final String USER_JOIN_EVENT = "userJoin" ; // 用户加入事件
public static final String TEST = "test" ;
private static int WebIMPort = 5001 ;
private static boolean imServerRunning = false ; // IM服务状态
private static ApplicationContext applicationContext ;
public static Map<String , Boolean> model = new HashMap<String,Boolean>();
public static int getWebIMPort() {
return WebIMPort;
}
public static void setWebIMPort(int webIMPort) {
WebIMPort = webIMPort;
}
public void setApplicationContext(ApplicationContext context){
applicationContext = context ;
}
public static ApplicationContext getContext(){
return applicationContext ;
}
public static void setIMServerStatus(boolean running){
imServerRunning = running ;
}
public static boolean getIMServerStatus(){
return imServerRunning;
}
}
编写 推送事件 SysEvent 类:
import org.springframework.context.ApplicationEvent;
/**
* @ClassName: SysEvent
* @Description: 事件
* @author Administrator
* @date Dec 10, 2020
* @version V1.0
*/
public class SysEvent extends ApplicationEvent
{
private static final long serialVersionUID = 1L;
public SysEvent(Object source)
{
super(source);
}
}
/**
* @ClassName: TestEvent
* @Description:测试
*/
public class TestEvent extends SysEvent{
private static final long serialVersionUID = 1L;
public TestEvent(Object source) {
super(source);
}
private String text;
public String getText() {
return text;
}
public void setText(String text) {
this.text = text;
}
}
编写监听SystemEventListener 类:
import java.io.IOException;
import java.io.Serializable;
import javax.annotation.Resource;
import org.apache.log4j.Logger;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationListener;
import org.springframework.stereotype.Component;
import com.alibaba.fastjson.JSON;
import com.huash.order.websocket.context.HuashContext;
import com.huash.order.websocket.event.SysEvent;
import com.huash.order.websocket.event.TestEvent;
import com.huash.order.websocket.service.WebSocketServer;
/**
* @ClassName: SystemEventListener
* @Description: 监听事件,执行操作
* @version V1.0
*/
@Component
@SuppressWarnings("all")
public class SystemEventListener implements ApplicationListener<SysEvent>
{
Logger logger = Logger.getLogger(SystemEventListener.class);
@Autowired
private WebSocketServer webSocketServer;
@Override
public void onApplicationEvent(SysEvent event)
{
logger.info(String.format("%s 监听到事件源:%s.", SystemEventListener.class.getName(), event.getSource()));
String es = event.getSource().toString();
switch( es ){
case HuashContext.TEST: test(event); break; // 事件
}
}
// test 可做详细内容....
private void test(SysEvent event)
{
TestEvent e = (TestEvent)event;
Message message = new Message();
message.setText(e.getText());
webSocketServer.broadcast(JSON.toJSONString(message));
}
}
添加一个消息文本 Message 类:
/**
* @ClassName: Message
* @Description: 消息文本
* @version V1.0
*/
public class Message {
//发送者name
public String from;
//接收者name
public String to = "-1";
//发送的文本
public String text;
//发送时间
@JSONField(format="yyyy-MM-dd HH:mm:ss")
public Date date;
public String getFrom() {
return from;
}
public void setFrom(String from) {
this.from = from;
}
public String getTo() {
return to;
}
public void setTo(String to) {
this.to = to;
}
public String getText() {
return text;
}
public void setText(String text) {
this.text = text;
}
public Date getDate() {
return date;
}
public void setDate(Date date) {
this.date = date;
}
}
封装返回数据类:
import java.io.Serializable;
import org.apache.commons.lang3.StringUtils;
public class ResultJson implements Serializable
{
private static final long serialVersionUID = 7646800798370648252L;
private Object data;
private String mess;
private String result = SUCCESS_DESC;
private int code = SUCCESS_CODE;
private Long timeStamp = System.currentTimeMillis();
private static final int SUCCESS_CODE = 200;
private static final String SUCCESS_DESC = "操作成功";
private static final int FAILURE_CODE = 10001;
private static final String FAILURE_DESC = "操作异常";
private static final int PARAMETER_EXCEPTION_CODE = 10002;
private static final String PARAMETER_EXCEPTION_DESC = "传参异常";
private static final int STATUS_EXCEPTION_CODE = 10009;
private static final String STATUS_EXCEPTION_DESC = "订单状态异常";
public static ResultJson success(Object data, String mess, String result) {
ResultJson resultJson = new ResultJson();
resultJson.setData(data);
resultJson.setMess(mess);
resultJson.setResult(result);
return resultJson;
}
public static ResultJson success() {
return new ResultJson();
}
public static ResultJson success(Object data, String mess) {
ResultJson responseJson = new ResultJson();
responseJson.setData(data);
responseJson.setMess(mess);
responseJson.setCode(SUCCESS_CODE);
return responseJson;
}
public static ResultJson success(Object data) {
return success(data, SUCCESS_DESC);
}
public static ResultJson success(String mess)
{
ResultJson responseJson = new ResultJson();
responseJson.setMess(StringUtils.isEmpty(mess) ? SUCCESS_DESC : mess);
responseJson.setCode(SUCCESS_CODE);
return responseJson;
}
public static ResultJson failure(String mess) {
return error(FAILURE_CODE, FAILURE_DESC, mess);
}
public static ResultJson statusFailure(String mess) {
return error(STATUS_EXCEPTION_CODE, STATUS_EXCEPTION_DESC, mess);
}
public static ResultJson parameterException(String msg)
{
return error(PARAMETER_EXCEPTION_CODE, PARAMETER_EXCEPTION_DESC, msg);
}
public static ResultJson error(Integer code, String result, String msg) {
ResultJson responseJson = new ResultJson();
responseJson.setMess(msg);
responseJson.setResult(result);
responseJson.setCode(code);
return responseJson;
}
public ResultJson() {
}
public Object getData() {
return data;
}
public void setData(Object data) {
this.data = data;
}
public String getMess() {
return mess;
}
public void setMess(String mess) {
this.mess = mess;
}
public String getResult() {
return result;
}
public void setResult(String result) {
this.result = result;
}
public Long getTimeStamp() {
return timeStamp;
}
public void setTimeStamp(Long timeStamp) {
this.timeStamp = timeStamp;
}
public int getCode() {
return code;
}
public void setCode(int code) {
this.code = code;
}
@Override
public String toString() {
return "ResultJson{" +
"data=" + data +
", mess='" + mess + '\'' +
", result='" + result + '\'' +
", code=" + code +
", timeStamp=" + timeStamp +
'}';
}
}
前端html 代码:
<!DOCTYPE HTML>
<html>
<head>
<base href="localhost://localhost:8080/">
<title>My WebSocket</title>
</head>
<body>
Welcome
<br />
<input id="text" type="text" />
<button onclick="send()">Send</button>
<button onclick="closeWebSocket()">Close</button>
<div id="message"></div>
</body>
<script type="text/javascript">
var websocket = null;
var user = '111';
//判断当前浏览器是否支持WebSocket
if ('WebSocket' in window) {
websocket = new WebSocket("ws://localhost:8080/webSocket/" + user);
} else {
alert('Not support websocket')
}
//连接发生错误的回调方法
websocket.onerror = function() {
setMessageInnerHTML("error");
};
//连接成功建立的回调方法
websocket.onopen = function(event) {
setMessageInnerHTML("open");
}
//接收到消息的回调方法
websocket.onmessage = function(event) {
setMessageInnerHTML(event.data);
}
//连接关闭的回调方法
websocket.onclose = function() {
setMessageInnerHTML("close");
}
//监听窗口关闭事件,当窗口关闭时,主动去关闭websocket连接,防止连接还没断开就关闭窗口,server端会抛异常。
window.onbeforeunload = function() {
websocket.close();
}
//将消息显示在网页上
function setMessageInnerHTML(innerHTML) {
document.getElementById('message').innerHTML += innerHTML + '<br/>';
}
//关闭连接
function closeWebSocket() {
websocket.close();
}
//发送消息
function send() {
var message = document.getElementById('text').value;
var text = {"text":message,"to":"-1"};
websocket.send(JSON.stringify(text));
}
</script>
</html>
然后服务端推送测试demo:
@Controller
public class TestController {
@RequestMapping(value ="test", method = RequestMethod.GET,
produces = MediaType.APPLICATION_JSON_UTF8_VALUE)
@ResponseBody
public ResultJson test()
{
TestEvent testEvent = new TestEvent(HuashContext.TEST);
testEvent.setText("测试");
HuashContext.getContext().publishEvent(testEvent);
return ResultJson.success("成功");
}
}
用spring 事件去推送消息内容,每一个事件继承Event并编写事件消息内容,然后通过监听listener监听事件,编写对应方法然后执行。
前端页面接收:
前端发送,后端接收:
整体项目结构: