spring使用websocket(转)
转载:https://blog.csdn.net/fffvdgjvbsfkb123456/article/details/116465394
WebSocket介绍
WebSocket 是 HTML5 开始提供的一种在单个 TCP 连接上进行全双工通讯的协议。
WebSocket 使得客户端和服务器之间的数据交换变得更加简单,允许服务端主动向客户端推送数据。在 WebSocket API 中,浏览器和服务器只需要完成一次握手,两者之间就直接可以创建持久性的连接,并进行双向数据传输。
在 WebSocket API 中,浏览器和服务器只需要做一个握手的动作,然后,浏览器和服务器之间就形成了一条快速通道。两者之间就直接可以数据互相传送。
协议有两部分,握手和数据传输。
握手是基于http协议的
来自客户端的握手形式如下:
来自服务器的握手形式如下:
websocket方法
引入POM依赖
<dependency> <groupId>org.springframework</groupId> <artifactId>spring-websocket</artifactId> <version>4.2.4.RELEASE</version> </dependency>
WebSocket入口配置
@Configuration @EnableWebSocket public class WebSocketConfig implements WebSocketConfigurer { /** * 注册handle * @see org.springframework.web.socket.config.annotation.WebSocketConfigurer#registerWebSocketHandlers(org.springframework.web.socket.config.annotation.WebSocketHandlerRegistry) */ @Override public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) { registry.addHandler(myHandler(), "/testHandler.do").addInterceptors(new WebSocketInterceptor()); registry.addHandler(myHandler(), "/socketJs/testHandler.do").addInterceptors(new WebSocketInterceptor()).withSockJS(); } @Bean public WebSocketHandler myHandler(){ return new MyMessageHandler(); } }
实现WebSocketConfigurer接口,重写registerWebSocketHandlers方法,这是一个核心实现方法,配置websocket入口,允许访问的域、注册Handler、SockJs支持和拦截器。
registry.addHandler注册和路由的功能,当客户端发起websocket连接,把/path交给对应的handler处理,而不实现具体的业务逻辑,可以理解为收集和任务分发中心。
addInterceptors,是为handler添加拦截器,可以在调用handler前后加入我们自己的逻辑代码。
创建拦截器
public class WebSocketInterceptor extends HttpSessionHandshakeInterceptor { @Override public boolean beforeHandshake(ServerHttpRequest request, ServerHttpResponse response, WebSocketHandler wsHandler, Map<String, Object> attributes) throws Exception { if (request instanceof ServletServerHttpRequest) { ServletServerHttpRequest serverHttpRequest = (ServletServerHttpRequest) request; // 获取参数 String userId = serverHttpRequest.getServletRequest().getParameter( "userId"); attributes.put("currentUser", userId); } return true; } // 初次握手访问后 @Override public void afterHandshake(ServerHttpRequest serverHttpRequest, ServerHttpResponse serverHttpResponse, WebSocketHandler webSocketHandler, Exception e) { } }
beforeHandshake,在调用handler前处理方法。常用在登录用户信息,绑定WebSocketSession,在handler里根据用户信息获取WebSocketSession发送消息。
Handler处理类
public class MyMessageHandler implements WebSocketHandler { /** * userMap:使用线程安全map存储用户连接webscoket信息 * * @since JDK 1.7 */ private final static Map<String, WebSocketSession> userMap = new new ConcurrentHashMap<String, WebSocketSession>(); /** * 关闭websocket时调用该方法 * * @see org.springframework.web.socket.WebSocketHandler#afterConnectionClosed(org.springframework.web.socket.WebSocketSession, * org.springframework.web.socket.CloseStatus) */ @Override public void afterConnectionClosed(WebSocketSession session, CloseStatus status) throws Exception { String userId = this.getUserId(session); if (StringUtils.isNoneBlank(userId)) { userMap.remove(userId); System.err.println("该" + userId + "用户已成功关闭"); } else { System.err.println("关闭时,获取用户id为空"); } } /** * 建立websocket连接时调用该方法 * * org.springframework.web.socket.WebSocketHandler#afterConnectionEstablished(org.springframework.web.socket.WebSocketSession) */ @Override public void afterConnectionEstablished(WebSocketSession session) throws Exception { String userId = this.getUserId(session); if (StringUtils.isNoneBlank(userId)) { userMap.put(userId, session); session.sendMessage(new TextMessage("建立服务端连接成功!")); } } /** * 客户端调用websocket.send时候,会调用该方法,进行数据通信 * * org.springframework.web.socket.WebSocketHandler#handleMessage(org.springframework.web.socket.WebSocketSession, * org.springframework.web.socket.WebSocketMessage) */ @Override public void handleMessage(WebSocketSession session, WebSocketMessage<?> message) throws Exception { String msg = message.toString(); String userId = this.getUserId(session); System.err.println("该" + userId + "用户发送的消息是:" + msg); message = new TextMessage("服务端已经接收到消息,msg=" + msg); session.sendMessage(message); } /** * 传输过程出现异常时,调用该方法 * * org.springframework.web.socket.WebSocketHandler#handleTransportError(org.springframework.web.socket.WebSocketSession, * java.lang.Throwable) */ @Override public void handleTransportError(WebSocketSession session, Throwable e) throws Exception { WebSocketMessage<String> message = new TextMessage("异常信息:" + e.getMessage()); session.sendMessage(message); } /** * * org.springframework.web.socket.WebSocketHandler#supportsPartialMessages() */ @Override public boolean supportsPartialMessages() { return false; } /** * sendMessageToUser:发给指定用户 * */ public void sendMessageToUser(String userId, String contents) { WebSocketSession session = userMap.get(userId); if (session != null && session.isOpen()) { try { TextMessage message = new TextMessage(contents); session.sendMessage(message); } catch (IOException e) { e.printStackTrace(); } } } /** * sendMessageToAllUsers:发给所有的用户 * */ public void sendMessageToAllUsers(String contents) { Set<String> userIds = userMap.keySet(); for (String userId : userIds) { this.sendMessageToUser(userId, contents); } } /** * getUserId:获取用户id * * @author liuchao * @param session * @return * @since JDK 1.7 */ private String getUserId(WebSocketSession session) { try { String userId = (String) session.getAttributes().get("currentUser"); return userId; } catch (Exception e) { e.printStackTrace(); } return null; }
客户端连接
<script type="text/javascript"> $(function(){ var host= window.location.host; var websocket; if ('WebSocket' in window) { websocket = new WebSocket("ws://"+host+"/testHandler.do?userId=9528"); } else if ('MozWebSocket' in window) { websocket = new MozWebSocket("ws://"+host+"/testHandler.do?userId=9528"); } else { websocket = new SockJS("http://"+host+"/socketJs/testHandler.do?userId=9528"); } websocket.onopen = function (evnt) { console.log("链接服务器成功!") }; websocket.onmessage = function (evnt) { console.log(evnt.data); $("#message").html(evnt.data); }; websocket.onerror = function (evnt) { console.log("websocket错误"); }; websocket.onclose = function (evnt) { console.log("与服务器断开了链接!") } $('#send').bind('click', function() { send(); }); function send(){ if (websocket != null) { var message = document.getElementById('message').value; websocket.send(message); } else { alert('未与服务器链接.'); } } }); </script>
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南