SSM WebSocket实现消息推送
简单的介绍下功能:当有新的公告发出时,用户的界面公告处便会“+1”。实现流程大致为:连接websocket后,开启一个线程对数据库中的数据进行轮询,如果数据库的数据变了,则发送变更后的数据到前端。
Maven包
<!-- websocket -->
<dependency>
<groupId>javax.websocket</groupId>
<artifactId>javax.websocket-api</artifactId>
<version>1.1</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>javax</groupId>
<artifactId>javaee-api</artifactId>
<version>7.0</version>
</dependency>
具体实现
前端js
- html
<a onClick="x_admin_show('公告','<%=basePath%>announce/turnToNewAnnounce')"><span id="count">公告</span><span id="num" style="color:#FF0000"></span></a>
- js
<script type="text/javascript">
var websocket = null;
var url=window.location.host;
//判断当前浏览器是否支持WebSocket
if ('WebSocket' in window) {
//建立连接,这里的/websocket ,注解中的那个值
//替换成自己的项目名
websocket = new WebSocket("ws://"+url+"/webSocket/"+${USER.id });
} else {
alert('当前浏览器 Not support websocket');
}
//连接发生错误的回调方法
websocket.onerror = function () {
console.log("WebSocket连接发生错误");
};
//连接成功建立的回调方法
websocket.onopen = function () {
console.log("WebSocket连接成功");
};
//接收到消息的回调方法
websocket.onmessage = function (event) {
//显示该用户有多少条没读
if(event.data == 0){
$("#num").text("");
}else{
$("#num").text("+"+event.data);
}
console.log(event.data + "\n");
};
//连接关闭的回调方法
websocket.onclose = function () {
console.log("WebSocket连接关闭");
};
//监听窗口关闭事件,当窗口关闭时,主动去关闭WebSocket连接,防止连接还没断开就关闭窗口,server端会抛异常。
window.onbeforeunload = function () {
closeWebSocket();
};
//关闭WebSocket连接
function closeWebSocket() {
websocket.close();
}
</script>
WebSocket.java
@ServerEndpoint(value = "/webSocket/{uid}")
public class WebSocket {
@Resource
UserService userService;
//在线人数
private static int onlineCount = 0;
private static Map<String, WebSocket> clients = new ConcurrentHashMap<String, WebSocket>();
//通过session可以给每个WebSocket长连接中的客户端发送数据
private Session session;
MyThread thread=new MyThread();
private String uid;
/**
* @throws Exception
* @ClassName: onOpen
* @Description: 开启连接的操作
*/
@OnOpen
public void onOpen(Session session,@PathParam(value="uid") String uid) throws Exception {
this.uid = uid;
this.session = session;
addOnlineCount();
clients.put(uid, this);
//连接开始时数据库的值传到前端
//想调用springmvc的service层方法操作数据库并不能采用注解的方式,故只好这样实现了
ApplicationContext act = ApplicationContextRegister.getApplicationContext();
userService=act.getBean(UserService.class);
PageData pd = new PageData();
pd.put("userId", uid);
PageData user = userService.findUserByUsername(pd);
sendMessageTo(user.getString("announce_count"), uid);
//开启一个线程对数据库中的数据进行轮询
thread.queryContainer(uid);
}
/**
* @ClassName: onClose
* @Description: 连接关闭的操作
*/
@OnClose
public void onClose() {
/* thread1.stopMe();*/
clients.remove(uid);
subOnlineCount();
thread.destroy();
}
/**
* @throws IOException
* @ClassName: onMessage
* @Description: 给服务器发送消息告知数据库发生变化
*/
@OnMessage
public void onMessage(String message) throws IOException {
JSONObject jsonTo = JSONObject.fromObject(message);
if (!jsonTo.get("To").equals("All")){
sendMessageTo("给一个人", jsonTo.get("To").toString());
}else{
sendMessageAll("给所有人");
}
}
/**
* @ClassName: OnError
* @Description: 出错的操作
*/
@OnError
public void onError(Throwable error) {
thread.destroy();
error.printStackTrace();
}
/**
* 发送给指定的人
* @param message
* @param To
* @throws IOException
*/
public void sendMessageTo(String message, String To) throws IOException {
// session.getBasicRemote().sendText(message);
//session.getAsyncRemote().sendText(message);
for (WebSocket item : clients.values()) {
if (item.uid.equals(To) )
item.session.getAsyncRemote().sendText(message);
}
}
/**
* 发送给所有人
* @param message
* @throws IOException
*/
public void sendMessageAll(String message) throws IOException {
for (WebSocket item : clients.values()) {
item.session.getAsyncRemote().sendText(message);
}
}
public static synchronized int getOnlineCount() {
return onlineCount;
}
public static synchronized void addOnlineCount() {
WebSocket.onlineCount++;
}
public static synchronized void subOnlineCount() {
WebSocket.onlineCount--;
}
public static synchronized Map<String, WebSocket> getClients() {
return clients;
}
}
MyThread
public class MyThread {
@Resource
UserService userService;
private ScheduledExecutorService pool;
private boolean stopMe = true;
private int num;
private int new_num;
public void stopMe() {
stopMe = false;
}
// private final SimpleDateFormat sdf=new SimpleDateFormat("yyyy-MM-dd
// HH:mm:ss");
public MyThread() {
pool = Executors.newScheduledThreadPool(10);
}
// 发现想调用springmvc的service层方法操作数据库并不能采用注解的方式,故只好这样实现了
public void queryContainer(String uid) {
ApplicationContext act = ApplicationContextRegister.getApplicationContext();
userService = act.getBean(UserService.class);
pool.scheduleAtFixedRate(new Runnable() {
WebSocket wbs = new WebSocket();
@Override
public void run() {
PageData pd = new PageData();
pd.put("userId", uid);
try {
PageData user = userService.findUserByUsername(pd);
num = Integer.parseInt(user.get("announce_count").toString());
while (stopMe) {
PageData userData = userService.findUserByUsername(pd);
new_num = Integer.parseInt(userData.get("announce_count").toString());
if (num != new_num) {
System.out.println("change");
num = new_num;
String msg = "" + num;
wbs.sendMessageTo(msg, uid);
}
}
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}, 2000, 2000, TimeUnit.MILLISECONDS);
}
public void destroy() {// 线程池停止后的方法
System.out.println("end *******************************************************************");
pool.shutdownNow();
}
}