环境
jdk1.8、tomcat8、浏览器谷歌77、IE11
websocket的实现有2种方式,一种为tomcat的websocket,比较简单,tomcat要求7以上,低版本浏览器可能有兼容问题,未测试。另外一种为spring整合websocket,spring的版本要求4.x以上,可以通过socketjs实现低版本浏览器兼容,参考地址:https://blog.csdn.net/weixin_44262390/article/details/100988545
pom.xml
<!-- 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>
websocket.js
前端脚本
var wb;
//脚本入口
$(function () {
wb_connect();
});
/**
* 连接 websocket
* @param func onopen要执行的函数,可以为空
*/
function wb_connect() {
wb = new WebSocket("ws://"+window.location.host+"/xmm/test");
//j接收服务端主动推送消息
wb.onmessage = function (event) {
if(event==""||event==null)
return;
}
if(event.data == null || event.data == ""){
return;
}
var keys= Object.keys(JSON.parse(event.data));
$.each(keys,function(index,item){
if(item=="xx"){
}
})
}
wb.onopen = function () {
// 开启心跳
ws_heart();
};
wb.onerror = function (e) {
console.log("webSocket发生错误");
};
wb.onclose = function (e) {
console.log("webSocket关闭");
};
}
/**
* 根据连接状态单线程连接 websocket
* @param func onopen要执行的函数,可以为空
*/
function wb_execute() {
//console.log("ws状态是:" + ws.readyState);
if (wb.readyState == 0) {
//连接中
var _old$open = wb.onopen;
wb.onopen = function (e) {
// 原本 onopen 里的代码先执行完毕
_old$open.apply(this, arguments);
};
} else if (wb.readyState == 1) {
//连接正常,暂未处理
} else if (wb.readyState == 2) {
//连接正在关闭,需要重新连接
var _old$close = wb.onclose;
wb.onclose = function (e) {
//原本 onclose 里的代码先执行完毕
_old$close.apply(this, arguments);
wb_connect();
};
} else if (wb.readyState == 3) {
//连接已关闭或者没有链接成功,需要重新连接
wb_connect();
}
}
var wb_heart_i = null;
/**
* websocket 每1分钟发一次心跳 判断连接是否正常
*/
function wb_heart() {
if (wb_heart_i) clearInterval(wb_heart_i);
wb_heart_i = setInterval(function () {
wb_execute();
}, 10000);
}
后端实现
import java.io.IOException;
import java.util.concurrent.ConcurrentHashMap;
import javax.servlet.http.HttpSession;
import javax.websocket.EndpointConfig;
import javax.websocket.OnClose;
import javax.websocket.OnError;
import javax.websocket.OnMessage;
import javax.websocket.OnOpen;
import javax.websocket.Session;
import javax.websocket.server.ServerEndpoint;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.common.EmptyUtil;
import com.dao.systemmanager.domain.SysUser;
//configurator = HttpSessionConfigurator.class 为了获取Httpsession
@ServerEndpoint(value = "/test", configurator = HttpSessionConfigurator.class)
public class WebServer {
private static ConcurrentHashMap<String, WebServer> webSocketSet = new ConcurrentHashMap<>();
private final Logger logger = LoggerFactory.getLogger(this.getClass());
//与某个客户端的连接会话,需要通过它来给客户端发送数据
private Session session;
private HttpSession httpSession;
private String id = "";
public HttpSession getHttpSession() {
return httpSession;
}
public void setHttpSession(HttpSession httpSession) {
this.httpSession = httpSession;
}
@OnOpen
public void onOpen(Session session,EndpointConfig config) {
httpSession = (HttpSession) config.getUserProperties().get(HttpSession.class.getName());
SysUser sysUser = (SysUser) httpSession.getAttribute("user");
if (EmptyUtil.isEmpty(sysUser)) {
return;
}
this.session = session;
webSocketSet.put(String.valueOf(sysUser.getId()), this);//加入set中
WebManager.add(this);
}
public void sendMessage(String message) throws IOException {
this.session.getBasicRemote().sendText(message); //同步方法
}
/**
* 发送信息给指定ID用户,如果用户不在线则返回不在线信息给自己
* @param message
* @param sendUserId
* @throws IOException
*/
public void sendtoUser(String message,String sendUserId) throws IOException {
if (webSocketSet.get(sendUserId) != null) {
webSocketSet.get(sendUserId).sendMessage(message);
} else {
//如果用户不在线则返回不在线信息给自己
sendtoUser("当前用户不在线",id);
}
}
@OnClose
public void onClose() {
WebManager.remove(this);
}
@OnMessage
public void onMessage(String message, Session session) {
System.out.print(message);
}
@OnError
public void onError(Session session, Throwable error) {
logger.info("webSocket发生错误");
}
}
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
public class WebManager {
public static List<WebServer> list = Collections.synchronizedList(new ArrayList<WebServer>());
//广播--发给所有用户
public static void broadCast(String message) {
for (WebServer ws : list) {
try {
ws.sendMessage(message);
} catch (IOException e) {
e.printStackTrace();
}
}
}
//发给指定用户
public static void msgPushToUser(String message,String sendUserId) {
for (WebServer ws : list) {
try {
ws.sendtoUser(message, sendUserId);
} catch (IOException e) {
e.printStackTrace();
}
}
}
public static void add(WebServer server) {
list.add(server);
}
public static void remove(WebServer server) {
list.remove(server);
}
}
import javax.servlet.http.HttpSession;
import javax.websocket.HandshakeResponse;
import javax.websocket.server.HandshakeRequest;
import javax.websocket.server.ServerEndpointConfig;
import javax.websocket.server.ServerEndpointConfig.Configurator;
// 为了获取Httpsession
public class HttpSessionConfigurator extends Configurator {
@Override
public void modifyHandshake(ServerEndpointConfig sec, HandshakeRequest request, HandshakeResponse response) {
HttpSession httpSession = (HttpSession) request.getHttpSession();
sec.getUserProperties().put(HttpSession.class.getName(), httpSession);
}
}
业务实现
//短信消息生成完成后查询短信消息与系统公告消息未读数量,推送到前端
List<WebServer> list = WebManager.list;
if (EmptyUtil.isNotEmpty(list)) {
for(WebServer server : list){
User user = (User)server.getHttpSession().getAttribute("user");
if (EmptyUtil.isEmpty(user )) {
logger.info("消息推送失败,当前登录用户信息为空,请重检查新登录");
return;
}
HashMap<String, Object> map = new HashMap<>();
map .put("xx", "xx");
//WebManager.broadCast(JSONObject.toJSONString(map));//广播
//发送消息给指定用户
WebManager.msgPushToUser(JSONObject.toJSONString(map),"");
}
}