使用springBoot集成的webSocket实现实时消息推送(模仿聊天)
java代码
package com.lsbj.biz.scoket;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Component;
import javax.websocket.*;
import javax.websocket.server.PathParam;
import javax.websocket.server.ServerEndpoint;
import java.io.IOException;
import java.util.Hashtable;
import java.util.List;
import java.util.Map;
/**
* @Auther: zs
* @Date: 2019/1/17 11:04
* @Description: *
* @ServerEndpoint 注解是一个类层次的注解,它的功能主要是将目前的类定义成一个websocket服务器端,
* 注解的值将被用于监听用户连接的终端访问URL地址,客户端可以通过这个URL来连接到WebSocket服务器端
*/
@ServerEndpoint("/ws/push/{userId}")
@Component
public class WebSocketDemo {
// Hashtablex线程安全的map用来存储一登录用户信息,key为用户id。
private static Hashtable<String, WebSocketDemo> userMap = new Hashtable<>();
// 静态变量,用来记录当前在线连接数。
private static int onlineCount = 0;
// 与客户端的连接会话。
private Session session;
// 与客户端的连接的用户id。
private String userId;
//连接打开时执行
@OnOpen
public void onOpen(Session session,@PathParam("userId") String userId) throws IOException{
System.out.println("新客户端接入,用户ID:" + userId);
System.out.println("在线人数:" + WebSocketDemo.onlineCount);
if(StringUtils.isNotBlank(userId)){
this.userId = userId;
this.session = session;
userMap.put(userId,this); // 加入set中
addOnlineCount(); // 在线数加1
}
System.out.println("在线人数:" + WebSocketDemo.onlineCount);
}
//连接关闭调用的方法
@OnClose
public void onClose() {
System.out.println("客户端关闭连接:"+this.userId);
userMap.remove(this.userId); // 从map中删除
subOnlineCount(); // 在线数减1
System.out.println("在线人数:" + WebSocketDemo.onlineCount);
}
//收到客户端消息后调用的方法
@OnMessage
public void onMessage(String message, Session session) {
if(StringUtils.isNotBlank(this.userId)){
if(!"ping".equals(message)){//不是心跳检测
//收到消息后可以去做一些具体的业务处理在推送,此处直接推送
sendAll("【"+this.userId+"】"+message);
}
}else{
System.out.println("当前客户未登陆:"+this.userId);
}
System.out.println("用户【"+this.userId+"】访问");
}
//发生错误时调用
@OnError
public void onError(Session session, Throwable error) {
error.printStackTrace();
}
public void sendMessage(String userId,String message){
try {
if(!StringUtils.isNotBlank(userId)){
System.out.println("客户ID不能为空");
return ;
}
for(Map.Entry<String, WebSocketDemo> entry : userMap.entrySet()){
if(entry.getKey().equals(userId)){
entry.getValue().getSession().getBasicRemote().sendText(message);
System.out.println("推送给用户【"+entry.getKey()+"】消息成功,消息为:" + message);
}
}
} catch (IOException e) {
e.printStackTrace();
}
}
public void sendMessage(List<String> userIds,String message){
try {
if(userIds == null || userIds.size() == 0){
System.out.println("客户ID不能为空");
return ;
}
for(Map.Entry<String, WebSocketDemo> entry : userMap.entrySet()){
if(userIds.contains(entry.getKey())){
entry.getValue().getSession().getBasicRemote().sendText(message);
System.out.println("推送给用户【"+entry.getKey()+"】消息成功,消息为:" + message);
}
}
} catch (IOException e) {
e.printStackTrace();
}
}
public void sendAll(String message){
try {
if(!StringUtils.isNotBlank(userId)){
System.out.println("客户ID不能为空");
return ;
}
for(Map.Entry<String, WebSocketDemo> entry : userMap.entrySet()){
entry.getValue().getSession().getBasicRemote().sendText(message);
System.out.println("推送给用户【"+entry.getKey()+"】消息成功,消息为:" + message);
}
} catch (IOException e) {
e.printStackTrace();
}
}
//获取连接人数
public static synchronized int getOnlineCount() {
return onlineCount;
}
//连接人数加一
public static synchronized void addOnlineCount() {
onlineCount+=1;
}
//连接人数减一
public static synchronized void subOnlineCount() {
if(onlineCount > 0){
onlineCount-=1;
}
}
public Session getSession() {
return session;
}
}
页面(HTML)
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8" />
<title>保存更新</title>
<script src="http://libs.baidu.com/jquery/2.0.0/jquery.js"></script>
<style>
input,textarea{
box-sizing: border-box;
-webkit-box-sizing: border-box;
-moz-box-sizing: border-box;
}
</style>
</head>
<body>
<input name="user" id="user" type="text" style="width: 100px"><button type="button" id="login">登录</button>
<form style="width: 300px;">
<input name="tenantIds" placeholder="" style="width:100%;" ><br>
<button type="button" id="send" style="width:100%;" >提交</button><br>
<div id="result" placeholder="结果提示" style="width:100%;height: 500px;border: gray solid 1px" ></div>
</form>
</body>
<script>
var webSocket = null;
$(document).ready(function () {
//模拟不同用户,实际开发中从session或redis里取
//判断当前浏览器是否支持WebSocket
$("#login").click(function () {
var user = $("#user").val();
if("WebSocket" in window) {
webSocket = new WebSocket("ws://localhost:8080/ws/push/" + user);
displayMsg('用户【' + user + '】尝试连接')
//连接发生错误的回调方法
webSocket.onerror = function(){
displayMsg("服务器连接【异常】");
};
//连接成功建立的回调方法
webSocket.onopen = function(event){
displayMsg('用户【'+user+'】服务器连接【成功】')
setInterval(function () {
webSocket.send("ping")
},10000)
}
//接收到消息的回调方法
webSocket.onmessage = function(event){
displayMsg(event.data);
}
//连接关闭的回调方法
webSocket.onclose = function(){
displayMsg("连接关闭");
}
//监听窗口关闭事件,当窗口关闭时,主动去关闭websocket连接,防止连接还没断开就关闭窗口,server端会抛异常。
window.onbeforeunload = function(){
websocket.close();
}
}else{
displayMsg('当前浏览器可能不支持WebSocket')
}
})
//关闭连接
function closeWebSocket(){
websocket.close();
}
$("#send").click(function () {
var message = $("input[name=tenantIds]").val();
if (message == "") {
displayMsg("访问消息不能为空")
} else {
webSocket.send(message);
displayMsg("已发送:" + message)
}
})
//将消息显示在网页上
function displayMsg(innerHTML){
document.getElementById('result').innerHTML += innerHTML + '<br/>';
}
})
</script>
</html>
实现实时推送(模仿聊天)
分别用:甲、乙、丙三个用户模拟登陆,丙统计登录人员
甲登录:
乙登录:
丙登录:
页面效果(甲发消息):
其他用户发消息和甲一样,都会推送,如乙回复消息:
关闭一个用户(关闭浏览器或退出)
会触发退出事件,关闭连接,修改在线人数
导包
在网上看很多都说需要引入整合包,(但是我这没引用也是正常的,也可能是其他地方引用了)
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-websocket</artifactId>
</dependency>