最近做项目需要后端实时推送消息给前端指定用户,比较理想的方案是集成websocket。网上找了一下,不是太老了,就是比较凌乱,基本都是集成tomcat的,没有集成JFinal推荐的undertow-websockets。最后结合官网文档和其他网友经验,终于搞定,发上来供其他人参考。
【后端代码】
1、添加undertow和对应webscoket集成到pom.xml
<!-- jfinal-undertow 开发、部署一体化 web 服务器 --> <dependency> <groupId>com.jfinal</groupId> <artifactId>jfinal-undertow</artifactId> <version>2.0</version> </dependency> <!-- 开发 WebSockets 时开启下面的依赖 --> <dependency> <groupId>io.undertow</groupId> <artifactId>undertow-websockets-jsr</artifactId> <version>2.0.28.Final</version> </dependency>
2、在undertow启动添加websocket集成
public static void main(String[] args) { UndertowServer.create(AppConfig.class) .configWeb(builder -> { // 配置WebSocket需使用ServerEndpoint注解 builder.addWebSocketEndpoint(WebSocket.class); }) .start(); System.out.println("系统服务启动完成......."); }
备注:AppConfig.class替换为自己的配置文件,WebSocket.class为后面添加的websocket方法
3、不拦截websocket的访问(AppConfig.class配置文件)
public void configHandler(Handlers me) { // 不拦截websocket me.add(new UrlSkipHandler("^/websocket.ws", false)); }
4、编写WebSocket.class的方法(代码可以直接使用,不过代码没考虑session分布式,只是单服务器用)
import com.alibaba.fastjson.JSONObject; import javax.websocket.*; import javax.websocket.server.PathParam; import javax.websocket.server.ServerEndpoint; import java.io.IOException; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; /** * 〈webSocket功能〉 * * @author foam103 * @create 2020/3/15 */ @ServerEndpoint("/websocket.ws/{userId}") public class WebSocket { private static Map<String, WebSocket> clients = new ConcurrentHashMap<String, WebSocket>(); private Session session; private String userId; @OnOpen public void onOpen(@PathParam("userId") String userId, Session session) throws IOException { this.userId = userId; this.session = session; clients.put(userId, this); //System.out.println("已连接"); } @OnClose public void onClose(Session session) { clients.remove(userId); } @OnError public void onError(Session session, Throwable error) { error.printStackTrace(); } @OnMessage public void onMessage(String message) { // message是json格式 JSONObject obj = JSONObject.parseObject(message); String user = obj.get("userId").toString(); String mes = obj.get("message").toString(); // 判断是否在线,如果在线发送信息 for (WebSocket item : clients.values()) { if (item.userId.equals(user)) { item.session.getAsyncRemote().sendText(mes); } } } }
5、其他业务地方触发消息发送(message可以用json格式,这样便于传递更多信息到前端)
// 引用websocket private WebSocket ws = new WebSocket(); // websocket推送回复消息 ws.onMessage("{\"userId\":\"" + receiver + "\",\"message\":{\"tp\":\"" + type + "\",\"mes\":\"" + content + "\"}}");
【前端代码】
1、编写js代码(setMessage方法需要自己写,其他的不动)
// websocket方法 function ws(userId) { var websocket = null; var host = document.location.host; var Protocol = window.location.protocol.split(':')[0]; //判断当前浏览器是否支持WebSocket if (window.WebSocket) { if (Protocol == 'https') { websocket = new WebSocket('wss://' + host + '/websocket.ws/' + userId); } else { websocket = new WebSocket('ws://' + host + '/websocket.ws/' + userId); } } else { alert('当前浏览器不支持websocket'); } //连接发生错误的回调方法 websocket.onerror = function () { //alert('WebSocket连接发生错误'); }; //连接成功建立的回调方法 websocket.onopen = function () { //alert('WebSocket连接成功'); } //接收到消息的回调方法 websocket.onmessage = function (event) { setMessage(event.data); } //连接关闭的回调方法 websocket.onclose = function () { //alert('WebSocket连接关闭'); } //监听窗口关闭事件,当窗口关闭时,主动去关闭websocket连接,防止连接还没断开就关闭窗口,server端会抛异常。 window.onbeforeunload = function () { websocket.close(); } //将消息显示在网页上 function setMessage(message) { //这里解析message,然后用js赋值给前端就行 } }
2、前端html页面调用
<script type="text/javascript" src="/plugins/js/ws.js"></script> // 适当的地方加载websocket ws(userid);
【结束语】
代码是最简单模式,很方便读懂,供大家参考。如有不完善,请大家多指教。