当前位置: 首页 > news >正文

websocket学习

一、基础使用

1、导入springboot关于websocket依赖

<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-websocket</artifactId> </dependency>

2、定义自己的websocket处理器

package clouddemo.handler; import org.springframework.web.socket.BinaryMessage; import org.springframework.web.socket.CloseStatus; import org.springframework.web.socket.TextMessage; import org.springframework.web.socket.WebSocketSession; import org.springframework.web.socket.handler.TextWebSocketHandler; import java.io.IOException; import java.util.concurrent.CopyOnWriteArraySet; public class MyWebSocketHandler extends TextWebSocketHandler { private static final CopyOnWriteArraySet<WebSocketSession> sessions = new CopyOnWriteArraySet<>(); /** * 建立连接 * @param session * @throws Exception */ @Override public void afterConnectionEstablished(WebSocketSession session) throws Exception { sessions.add(session); System.out.println("连接建立: " + session.getId()); } /** *广播消息 * @param session * @param message * @throws Exception */ @Override protected void handleTextMessage(WebSocketSession session, TextMessage message) throws Exception { String text = message.getPayload(); System.out.println("收到消息: " + text); // 广播给所有在线用户 for (WebSocketSession s : sessions) { if (s.isOpen()) { s.sendMessage(new TextMessage("Echo: " + text)); } } } /** * 广播二进制数据 * @param session * @param message */ @Override protected void handleBinaryMessage(WebSocketSession session, BinaryMessage message) { byte[] bytes = message.getPayload().array(); System.out.println("收到二进制数据,长度: " + bytes.length + " 字节"); // 回传二进制(例如原样返回) try { session.sendMessage(new BinaryMessage(bytes)); } catch (IOException e) { throw new RuntimeException(e); } } /** * 关闭连接 * @param session * @param status * @throws Exception */ @Override public void afterConnectionClosed(WebSocketSession session, CloseStatus status) throws Exception { sessions.remove(session); System.out.println("连接关闭: " + session.getId()); } /** * 处理底层 WebSocket 传输过程中发生的异常或错误。 * @param session * @param exception * @throws Exception */ @Override public void handleTransportError(WebSocketSession session, Throwable exception) throws Exception { System.err.println("传输错误: " + exception.getMessage()); session.close(CloseStatus.SERVER_ERROR); } }

3、websocket配置

package clouddemo.config; import clouddemo.handler.MyWebSocketHandler; 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; @Configuration @EnableWebSocket public class WebSocketConfig implements WebSocketConfigurer { @Override public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) { registry.addHandler(myWebSocketHandler(), "/ws") .setAllowedOrigins("*"); // 生产环境应限制来源 } public MyWebSocketHandler myWebSocketHandler() { return new MyWebSocketHandler(); } }

4、前端代码

<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>WebSocket + jQuery</title> <script src="https://code.jquery.com/jquery-3.6.0.min.js"></script> </head> <body> <input type="text" id="messageInput" placeholder="输入消息"> <button id="sendBtn">发送</button> <div id="messages"></div> <script> $(document).ready(function () { // 假设后端运行在 localhost:8080 const ws = new WebSocket("ws://localhost:8080/ws"); ws.onopen = function (event) { $('#messages').append('<p style="color:green;">✅ 连接已建立</p>'); }; ws.onmessage = function (event) { $('#messages').append('<p><strong>收到:</strong> ' + event.data + '</p>'); }; ws.onclose = function (event) { $('#messages').append('<p style="color:red;">❌ 连接已关闭</p>'); }; ws.onerror = function (error) { console.error("WebSocket 错误:", error); $('#messages').append('<p style="color:red;">⚠️ WebSocket 发生错误</p>'); }; $('#sendBtn').click(function () { const msg = $('#messageInput').val().trim(); if (msg) { ws.send(msg); $('#messageInput').val(''); } }); // 支持回车发送 $('#messageInput').keypress(function (e) { if (e.which === 13) { $('#sendBtn').click(); } }); }); </script> </body> </html>

5、测试

二、实现一对一单聊
1、 用户-Session 管理器(线程安全)

package com.atbdu.singlechat.manager; import org.springframework.web.socket.WebSocketSession; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; public class UserSessionManager { private static final Map<String, WebSocketSession> userSessions = new ConcurrentHashMap<>(); /** * 添加userId和session映射 * @param userId * @param session */ public static void add(String userId,WebSocketSession session){ userSessions.put(userId,session); } /** * 移除userId对应的session * @param userId */ public static void remove(String userId){ userSessions.remove(userId); } /** * 获取userId对应的session * @param userId */ public static WebSocketSession getSession(String userId){ return userSessions.get(userId); } /** * 判断用户是否建立连接 * @param userId * @return */ public static boolean isOnline(String userId){ WebSocketSession webSocketSession = userSessions.get(userId); return webSocketSession != null && webSocketSession.isOpen(); } }

2、握手拦截器(绑定用户身份)

package com.atbdu.singlechat.interceptor; import org.springframework.http.server.ServerHttpRequest; import org.springframework.http.server.ServerHttpResponse; import org.springframework.web.socket.WebSocketHandler; import org.springframework.web.socket.server.HandshakeInterceptor; import java.util.Map; public class AuthHandshakeInterceptor implements HandshakeInterceptor { @Override public boolean beforeHandshake(ServerHttpRequest request, ServerHttpResponse response, WebSocketHandler wsHandler, Map<String, Object> attributes) throws Exception { String url = request.getURI().getQuery(); if (url.contains("userId")){ String userId = url.split("userId=")[1].split("&")[0]; attributes.put("userId",userId); } return true; } @Override public void afterHandshake(ServerHttpRequest request, ServerHttpResponse response, WebSocketHandler wsHandler, Exception exception) { } }

3、WebSocket 处理器(处理私聊消息)

package com.atbdu.singlechat.handler; import com.alibaba.fastjson2.JSON; import com.alibaba.fastjson2.JSONObject; import com.atbdu.singlechat.manager.UserSessionManager; import org.springframework.web.socket.BinaryMessage; import org.springframework.web.socket.CloseStatus; import org.springframework.web.socket.TextMessage; import org.springframework.web.socket.WebSocketSession; import org.springframework.web.socket.handler.TextWebSocketHandler; import java.io.IOException; import java.util.Map; import java.util.concurrent.CopyOnWriteArraySet; /** * 实现一对一聊天 */ public class MyWebSocketHandler extends TextWebSocketHandler { private static final CopyOnWriteArraySet<WebSocketSession> sessions = new CopyOnWriteArraySet<>(); /** * 建立连接 * @param session * @throws Exception */ @Override public void afterConnectionEstablished(WebSocketSession session) throws Exception { Map<String, Object> attributes = session.getAttributes(); String userId = (String) attributes.get("userId"); if (userId != null){ UserSessionManager.add(userId,session); System.out.println("用户:"+userId +"上线"); session.sendMessage(new TextMessage("✅ 连接成功,你的ID是: " + userId)); }else { session.close(CloseStatus.NOT_ACCEPTABLE); } } /** *广播消息 * @param session * @param message * @throws Exception */ @Override protected void handleTextMessage(WebSocketSession session, TextMessage message) throws Exception { String text = message.getPayload(); System.out.println("收到消息: " + text); JSONObject json = JSON.parseObject(text); String from = (String) session.getAttributes().get("userId"); //用户id String to = (String) json.get("to"); //目标id String content = json.getString("content"); // 消息内容 if (to == null || content == null) { session.sendMessage(new TextMessage("❌ 消息格式错误")); return; } //构造发送给对方的消息 JSONObject reply = new JSONObject(); reply.put("from", from); reply.put("content", content); reply.put("type", "private"); //把消息发送给目标用户 WebSocketSession targetSession = UserSessionManager.getSession(to); if (targetSession != null && targetSession.isOpen()){ //发送消息 targetSession.sendMessage(new TextMessage(JSON.toJSONString(reply))); // 可选:给发送方回执(已送达) JSONObject result = new JSONObject(); result.put("type","public"); result.put("content","✅ 消息已发送给 " + to); session.sendMessage(new TextMessage(JSON.toJSONString(result))); }else { JSONObject result = new JSONObject(); result.put("type","public"); result.put("content","❌ 用户 " + to + " 不在线"); session.sendMessage(new TextMessage(JSON.toJSONString(result))); } } /** * 广播二进制数据 * @param session * @param message */ @Override protected void handleBinaryMessage(WebSocketSession session, BinaryMessage message) { byte[] bytes = message.getPayload().array(); System.out.println("收到二进制数据,长度: " + bytes.length + " 字节"); // 回传二进制(例如原样返回) try { session.sendMessage(new BinaryMessage(bytes)); } catch (IOException e) { throw new RuntimeException(e); } } /** * 关闭连接 * @param session * @param status * @throws Exception */ @Override public void afterConnectionClosed(WebSocketSession session, CloseStatus status) throws Exception { String userId = (String) session.getAttributes().get("userId"); if (userId != null) { UserSessionManager.remove(userId); System.out.println("用户下线: " + userId); } } /** * 处理底层 WebSocket 传输过程中发生的异常或错误。 * @param session * @param exception * @throws Exception */ @Override public void handleTransportError(WebSocketSession session, Throwable exception) throws Exception { String userId = (String) session.getAttributes().get("userId"); System.err.println("用户:"+userId+">>>传输错误: " + exception.getMessage()); session.close(CloseStatus.SERVER_ERROR); } }

4、WebSocket 配置

package com.atbdu.singlechat.config; import com.atbdu.singlechat.handler.MyWebSocketHandler; import com.atbdu.singlechat.interceptor.AuthHandshakeInterceptor; 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; @Configuration @EnableWebSocket public class WebSocketConfig implements WebSocketConfigurer { @Override public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) { registry.addHandler(myWebSocketHandler(), "/ws") .addInterceptors(myAuthHandshakeInterceptor()) .setAllowedOrigins("*"); // 生产环境应限制来源 } public MyWebSocketHandler myWebSocketHandler() { return new MyWebSocketHandler(); } public AuthHandshakeInterceptor myAuthHandshakeInterceptor(){ return new AuthHandshakeInterceptor(); } }

5、前端示例(jQuery + 原生 WebSocket)

<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>一对一聊天</title> <script src="https://code.jquery.com/jquery-3.6.0.min.js"></script> </head> <body> <p>当前用户ID: <input id="myId" value="alice"> (模拟登录)</p> <p>发送给: <input id="toUser" value="bob"></p> <p>消息: <input id="msgInput" placeholder="输入消息"></p> <button onclick="connect()">连接</button> <button onclick="send()">发送</button> <div id="chatBox" style="margin-top:20px; height:300px; overflow:auto;"></div> <script> let ws; function connect() { const myId = $('#myId').val().trim(); if (!myId) { alert('请输入自己的ID'); return; } ws = new WebSocket(`ws://localhost:8080/ws?userId=${encodeURIComponent(myId)}`); ws.onmessage = function(e) { const data = JSON.parse(e.data); if (data.type === 'private') { $('#chatBox').append(`<p><strong>${data.from}:</strong> ${data.content}</p>`); } else { $('#chatBox').append(`<p style="color:gray;">${data.content}</p>`); } $('#chatBox').scrollTop($('#chatBox')[0].scrollHeight); }; ws.onopen = () => $('#chatBox').append('<p style="color:green;">✅ 连接成功</p>'); ws.onclose = () => $('#chatBox').append('<p style="color:red;">❌ 连接断开</p>'); } function send() { const to = $('#toUser').val().trim(); const msg = $('#msgInput').val().trim(); if (!to || !msg || !ws || ws.readyState !== WebSocket.OPEN) { alert('请先连接并填写完整信息'); return; } ws.send(JSON.stringify({ to: to, content: msg })); $('#msgInput').val(''); } // 回车发送 $('#msgInput').keypress(e => { if (e.which === 13) send(); }); </script> </body> </html>

6、测试



7、关闭连接(session.close())

发送一条信息给自己,指定信息的类型,当遇到这个类型,即关闭

//**关闭连接** function closeConnection() { const to = $('#myId').val().trim();; const msg = '关闭连接'; const type = 'close'; ws.send(JSON.stringify({ to: to,type:type, content: msg })); $('#msgInput').val(''); }

其中发送信息的方法中,加入关闭连接的判断


三、群聊就是把userId换成房间号,然后分别给这个房间号对应的WebSocketSession发送信息就行了

http://www.jsqmd.com/news/285713/

相关文章:

  • 【课程设计/毕业设计】基于springboot的企业日报管理日报管理系统设计与实现【附源码、数据库、万字文档】
  • 第十五章 故障处理与运维
  • 从零开始学电脑:新手入门完全指南 - 指南
  • 2025.12.20 作业 - # P1246 编码
  • 【Da】调色面板
  • 学习AI编程
  • 235556
  • 打开CMD的方法和常用Dos命令
  • 【HTML】前端工具箱建立【文本处理/JSON程序/加解密/校验和/ASCII/时间戳转换等】【附完整源代码】
  • HRCSA项目结课
  • Java毕设选题推荐:基于springboo酒店客房管理系统民宿管理系统基于springboot的民宿客房管理系统【附源码、mysql、文档、调试+代码讲解+全bao等】
  • send-proxy vs send-proxy-v2 vs send-proxy-v2-ssl --2
  • 计算机Java毕设实战-基于Java+Springboot+Vue的民宿管理系统基于springboot的民宿客房管理系统【完整源码+LW+部署说明+演示视频,全bao一条龙等】
  • 第九章 数据一致性与分布式事务
  • 第八章 微服务通信实现
  • Java计算机毕设之基于springboot的民宿客房管理系统酒店客房管理系统设计(完整前后端代码+说明文档+LW,调试定制等)
  • autodl 上PaddleOCR-VL 部署(2026年1月22日亲测可用)
  • 【毕业设计】基于springboot的日报管理系统设计与实现(源码+文档+远程调试,全bao定制等)
  • 欧洲百年品牌瀚德凯尔:专注座椅电梯,提升老年人生活质量
  • Java毕设项目:基于springboot的民宿客房管理系统(源码+文档,讲解、调试运行,定制等)
  • 第七章 服务拆分与边界定义
  • 【计算机毕业设计案例】基于springboot的民宿房间预约管理系统设计与实现民宿客房管理系统(程序+文档+讲解+定制)
  • 【毕业设计】基于springboot的民宿客房管理系统(源码+文档+远程调试,全bao定制等)
  • C++大模型SDK开发实录(三):流式交互协议SSE解析与httplib实现原理
  • 算法围猎下的App渠道归因如何去伪存真?
  • 【课程设计/毕业设计】java基于springboot的民宿预约管理平台系统基于springboot的民宿客房管理系统【附源码、数据库、万字文档】
  • 我花6千块考下PMP一年后,聊聊它到底值不值
  • 系统规划与管理师必看:2026年监控工具选型与实施指南
  • 151. 反转字符串中的单词-day08
  • VL22 根据状态转移图达成时序电路