WebSocket为浏览器和服务端提供了双工异步通信功能。即浏览器可以向服务端发送消息,服务端也可以向浏览器发送消息。
应用于web项目中,多数情况前端需要实时的数据获取。即服务端向客户端实时的发送消息。
操作步骤如下:
项目目录如下:
1、运用IDE新建一个springboot项目,勾选Web、WebSocket、Thymeleaf等依赖如下图:
2、编写前端页面(客户端)与后台(服务端)通信握手拦截器。此处可记录当前建立连接的用户信息,用于定向信息发送的动能前提。代码如下
package com.example.websocket_springboot.config.websocket; import org.springframework.http.server.ServerHttpRequest; import org.springframework.http.server.ServerHttpResponse; import org.springframework.stereotype.Component; import org.springframework.web.socket.WebSocketHandler; import org.springframework.web.socket.server.support.HttpSessionHandshakeInterceptor; import java.util.Map; /** * @description 前端页面与后台通信握手拦截器,可用于完善定向发送信息功能。 * @author jane */ @Component public class HandshakeInterceptor extends HttpSessionHandshakeInterceptor { @Override public boolean beforeHandshake(ServerHttpRequest request, ServerHttpResponse response, WebSocketHandler wsHandler, Map<String, Object> attributes) throws Exception { //此处可将用户信息放入WebSocketSession的属性当中,以便定向发送消息。 attributes.put("WEBSOCKET_USERID",1L); return true; } @Override public void afterHandshake(ServerHttpRequest request, ServerHttpResponse response, WebSocketHandler wsHandler, Exception ex) { super.afterHandshake(request, response, wsHandler, ex); } }3、编写WebSocket配置类,比如绑定客户端端点url
package com.example.websocket_springboot.config.websocket; import com.example.websocket_springboot.handler.DemoHandler; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Configuration; import org.springframework.web.socket.config.annotation.EnableWebSocket; import org.springframework.web.socket.config.annotation.WebSocketConfigurer; import org.springframework.web.socket.config.annotation.WebSocketHandlerRegistry; /** * @description webSocket配置类,绑定前端连接端点url及其他信息 * @author jane */ @Configuration @EnableWebSocket public class WebSocketConfig implements WebSocketConfigurer { @Autowired private DemoHandler demoHandler; @Autowired private HandshakeInterceptor handshakeInterceptor; @Override public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) { //绑定前端连接端点url registry.addHandler(demoHandler, "/webSocket/demo").addInterceptors(handshakeInterceptor).withSockJS(); } }
4、自定义实现WebSocket处理类
package com.example.websocket_springboot.handler; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.stereotype.Component; import org.springframework.web.socket.*; import java.util.ArrayList; /** * @description webSocket处理类 * @author jane */ @Component public class DemoHandler implements WebSocketHandler { private static final Logger logger = LoggerFactory.getLogger(DemoHandler.class); private static final ArrayList<WebSocketSession> users; static { users = new ArrayList<>(); } @Override public void afterConnectionEstablished(WebSocketSession webSocketSession) throws Exception { logger.info("系统WebSocket连接已建立!"); //此处可添加客户端接收用户 logger.info(webSocketSession.getAttributes().get("WEBSOCKET_USERID").toString()); users.add(webSocketSession); } /** * 给指定用户发送信息 * @param webSocketSession * @param webSocketMessage * @throws Exception */ @Override public void handleMessage(WebSocketSession webSocketSession, WebSocketMessage<?> webSocketMessage) throws Exception { //获取指定用户ID Long userId = (Long) webSocketSession.getAttributes().get("WEBSOCKET_USERID"); String message; logger.info("处理推送的消息"); //判断客户端是否消息发送,不需要客户端与客户端的单向通信,此处可省略。 if (!webSocketMessage.getPayload().equals("undefined")){ message = "client 发送的消息为:" + webSocketMessage.getPayload(); }else { message = "推送测试信息 ---" + System.currentTimeMillis(); } sendMessageToUser(userId, new TextMessage(message)); } @Override public void handleTransportError(WebSocketSession webSocketSession, Throwable throwable) throws Exception { if (webSocketSession.isOpen()) { webSocketSession.close(); } logger.error("系统WebSocket传输错误,连接关闭!用户ID:" + webSocketSession.getAttributes().get("WEBSOCKET_USERID"), throwable); //移除异常用户信息 users.remove(webSocketSession); } @Override public void afterConnectionClosed(WebSocketSession webSocketSession, CloseStatus closeStatus) throws Exception { } @Override public boolean supportsPartialMessages() { return false; } public void sendMessageToUser(Long userId, TextMessage message) { logger.info("发送消息至用户!"); for (WebSocketSession user : users) { if (user.getAttributes().get("WEBSOCKET_USERID").equals(userId)) { sendSocketSessionMsg(user, message); } } } /** * 发送消息 * @param user 接收用户 * @param message 消息 */ private boolean sendSocketSessionMsg(WebSocketSession user, TextMessage message) { String msg = message.getPayload(); boolean sendSuccess = true; try { if (user.isOpen()) { synchronized (user) { user.sendMessage(message); } } else { logger.error("WebSocket连接未打开,系统消息推送失败:" + msg); sendSuccess = false; } } catch (Exception e) { logger.error("系统消息推送失败:" + msg, e); sendSuccess = false; } return sendSuccess; } }
5、配置MVC页面访问的配置类
package com.example.websocket_springboot.config.mvc; import org.springframework.context.annotation.Configuration; import org.springframework.web.servlet.config.annotation.ViewControllerRegistry; import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; /** * @description 配置MVC页面访问 * @author jane */ @Configuration public class WebMvcConfig implements WebMvcConfigurer { /** * 设置页面访问 * @param registry */ @Override public void addViewControllers(ViewControllerRegistry registry) { registry.addViewController("/demo").setViewName("/demo"); } /* *//** * 配置静态访问资源 * @param registry *//* @Override public void addResourceHandlers(ResourceHandlerRegistry registry) { registry.addResourceHandler("/**").addResourceLocations("classpath:/static/"); }*/ }
6、前端添加demo.html页面,代码如下:
<!DOCTYPE html> <html xmlns="http://www.w3.org/1999/xhtml" xmlns:th="http://www.thymeleaf.org" > <head> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /> <title>测试webSocket</title> </head> <body> <div>消息</div> <div id="message"></div> <div> <input id="msg"> <button οnclick="sendMsg()">发送消息</button> </div> </body> <script th:src="@{../static/plugins/sockjs.min.js}"></script> <script th:src="@{../static/plugins/jquery.min.js}"></script> <script th:src="@{../static/js/demo.js}"></script> </html>
js文件如下:
var webSocket= null; $(function() { test(); }); function test() { webSocket = new SockJS("/webSocket/demo"); webSocket.onopen = function () { webSocket.send(); }; webSocket.onmessage = function (e) { if (e.data) { var data = e.data; $("#message").text(data); } }; webSocket.onclose = function () { console.log('close run status socket'); }; webSocket.onerror = function () { console.error("链接出现异常,请检查服务器是否正常运行"); }; } function sendMsg() { if (webSocket.readyState == SockJS.OPEN){ var msg = $("#msg").val(); webSocket.send(msg); }else { alert("连接失败!") } }
自行添加sockjs.min.js和jquery.min.js前端脚本。