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

WebSocket实战:构建Spring Boot实时聊天应用

WebSocket是一种在单个TCP连接上进行全双工通信的协议,它使得客户端和服务器之间的数据交换变得更加简单,允许服务端主动向客户端推送数据。本文将基于Spring Boot框架,详细介绍如何实现一个完整的WebSocket聊天应用。

一、WebSocket协议简介

1.1 传统HTTP协议的局限性

  • 单向通信:只能由客户端发起请求
  • 轮询效率低:需要不断向服务器发送请求
  • 实时性差:无法实现服务器主动推送

1.2 WebSocket的优势

  • 全双工通信​:客户端和服务器可以同时发送和接收数据
  • 低延迟​:建立连接后,数据传输延迟显著降低
  • 减少带宽消耗​:避免了不必要的HTTP头部开销
  • 持久连接​:一次握手,持久连接

二、项目环境搭建

2.1 Maven依赖配置

pom.xml中配置必要的依赖:

<parent><groupId>org.springframework.boot</groupId>
-   <artifactId>spring-boot-starter-parent</artifactId>
-   <version>2.5.4</version>
</parent><dependencies><!-- Spring Boot核心依赖 --><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-websocket</artifactId></dependency><!-- Web支持 --><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><!-- 模板引擎 --><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-thymeleaf</artifactId></dependency><!-- JSON处理 --><dependency><groupId>com.google.code.gson</groupId><artifactId>gson</artifactId><version>2.7</version></dependency>
</dependencies>

2.2 主启动类

@SpringBootApplication
public class WebSocketApplication {public static void main(String[] args) {SpringApplication.run(WebSocketApplication.class, args);}
}

三、核心组件实现

3.1 消息实体类

定义消息传输对象:

public class Message {//注解表示该属性可以被序列化也可以被反序列化@Exposeprivate String from; //发送者@Exposeprivate String to; //接收者@Exposeprivate String message; //内容// 省略getter/setter方法
}

3.2 WebSocket配置类

配置WebSocket处理器和拦截器:

@Component
@EnableWebSocket
public class WebSocketConfig implements WebSocketConfigurer {@Autowiredprivate ChatHandler chatHandler;@Autowiredprivate ChatHandshakeInterceptor chatHandshakeInterceptor;@Overridepublic void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {registry.addHandler(chatHandler, "/chat")   //与/chat 建立websocket连接.addInterceptors(chatHandshakeInterceptor);}
}

3.3 握手拦截器

在连接建立前后进行拦截处理:

@Component
public class ChatHandshakeInterceptor implements HandshakeInterceptor {@Overridepublic boolean beforeHandshake(ServerHttpRequest request, ServerHttpResponse response, WebSocketHandler wsHandler, Map<String, Object> attributes) {// 可以进行身份验证等操作return true; // 放行连接}@Overridepublic void afterHandshake(ServerHttpRequest request, ServerHttpResponse response, WebSocketHandler wsHandler, Exception exception) {// 握手成功后的处理}
}

四、核心业务逻辑实现

4.1 WebSocket消息处理器

@Component
public class ChatHandler implements WebSocketHandler {// 保存所有用户连接会话public static final Map<String, WebSocketSession> USER_SOCKET_SESSION = new HashMap<>();/​**​* 连接建立成功回调*/@Overridepublic void afterConnectionEstablished(WebSocketSession session) throws Exception {USER_SOCKET_SESSION.put(session.getId(), session);System.out.println("用户连接成功,会话ID: " + session.getId());}/​**​* 处理接收到的消息*/@Overridepublic void handleMessage(WebSocketSession session, WebSocketMessage<?> message) throws Exception {String payload = message.getPayload().toString();System.out.println("接收到消息: " + payload);// 反序列化消息Message msg = GsonUtils.fromJson(payload, Message.class);// 根据消息类型进行不同处理if ("All".equals(msg.getTo())) {// 群发消息sendMessageToAll(msg);} else {// 私聊消息sendMessageToUser(msg.getTo(), msg);}}/​**​* 群发消息给所有用户*/public void sendMessageToAll(Message message) throws IOException {for (WebSocketSession session : USER_SOCKET_SESSION.values()) {if (session.isOpen()) {session.sendMessage(new TextMessage(GsonUtils.toJson(message)));}}}/​**​* 发送消息给指定用户*/public void sendMessageToUser(String userId, Message message) throws IOException {WebSocketSession session = USER_SOCKET_SESSION.get(userId);if (session != null && session.isOpen()) {session.sendMessage(new TextMessage(GsonUtils.toJson(message)));}}/​**​* 处理传输错误*/@Overridepublic void handleTransportError(WebSocketSession session, Throwable exception) throws Exception {System.out.println("连接出错: " + exception.getMessage());USER_SOCKET_SESSION.remove(session.getId());if (session.isOpen()) {session.close();}}/​**​* 连接关闭回调*/@Overridepublic void afterConnectionClosed(WebSocketSession session, CloseStatus closeStatus) throws Exception {USER_SOCKET_SESSION.remove(session.getId());System.out.println("用户断开连接,会话ID: " + session.getId());}@Overridepublic boolean supportsPartialMessages() {return false;}
}

4.2 JSON工具类

public class GsonUtils {private static final Gson GSON = new GsonBuilder().setDateFormat("yyyy-MM-dd HH:mm:ss").setPrettyPrinting().create();public static String toJson(Object object) {return GSON.toJson(object);}public static <T> T fromJson(String json, Class<T> classOfT) {return GSON.fromJson(json, classOfT);}
}

五、前端实现

5.1 HTML页面

<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><title>WebSocket聊天室</title><style>.chat-container {width: 400px;margin: 0 auto;border: 1px solid #ccc;padding: 20px;}.messages {height: 300px;border: 1px solid #eee;overflow-y: auto;margin-bottom: 10px;padding: 10px;}.input-group {display: flex;}#message {flex: 1;padding: 5px;}button {padding: 5px 15px;margin-left: 10px;}</style>
</head>
<body><div class="chat-container"><div class="messages" id="messages"></div><div class="input-group"><input type="text" id="message" placeholder="输入消息..."><button onclick="sendMessage()">发送</button></div></div><script>// WebSocket连接管理class ChatClient {constructor() {this.websocket = null;this.connect();}connect() {if ("WebSocket" in window) {this.websocket = new WebSocket("ws://localhost:8080/chat");this.websocket.onopen = (event) => {this.addMessage("系统", "连接服务器成功");};this.websocket.onmessage = (event) => {const data = JSON.parse(event.data);this.addMessage(data.from, data.message);};this.websocket.onclose = (event) => {this.addMessage("系统", "连接已断开");};this.websocket.onerror = (event) => {this.addMessage("系统", "连接错误");};} else {alert("浏览器不支持WebSocket");}}sendMessage(message, to = "All") {if (this.websocket && this.websocket.readyState === WebSocket.OPEN) {const data = {message: message,from: "用户" + Math.random().toString(36).substr(2, 5),to: to};this.websocket.send(JSON.stringify(data));}}addMessage(from, message) {const messagesDiv = document.getElementById("messages");const messageDiv = document.createElement("div");messageDiv.innerHTML = `<strong>${from}:</strong> ${message}`;messagesDiv.appendChild(messageDiv);messagesDiv.scrollTop = messagesDiv.scrollHeight;}}// 初始化聊天客户端const chatClient = new ChatClient();function sendMessage() {const messageInput = document.getElementById("message");const message = messageInput.value.trim();if (message) {chatClient.sendMessage(message);messageInput.value = "";}}// 回车发送消息document.getElementById("message").addEventListener("keypress", function(e) {if (e.key === "Enter") {sendMessage();}});</script>
</body>
</html>

5.2 页面控制器

@Controller
public class PageController {@RequestMapping("/chat")public String chatPage() {return "chat";}
}

六、WebSocket生命周期详解

6.1 连接建立过程

  1. HTTP握手​:客户端发起WebSocket连接请求
  2. 协议升级​:服务器响应101状态码切换协议
  3. 连接建立​:双向通信通道建立

6.2 消息处理流程

  1. 消息接收​:通过handleMessage方法处理 incoming消息
  2. 消息解析​:将JSON数据反序列化为Java对象
  3. 业务处理​:根据消息类型进行路由分发
  4. 消息发送​:将处理结果发送给目标用户

6.3 连接关闭处理

  • 正常关闭​:客户端主动断开连接
  • 异常关闭​:网络异常或服务器错误
  • 资源清理​:及时移除会话记录,释放资源

七、进阶功能扩展

7.1 用户身份认证

// 在握手拦截器中添加认证逻辑
@Override
public boolean beforeHandshake(ServerHttpRequest request, ServerHttpResponse response, WebSocketHandler wsHandler, Map<String, Object> attributes) {// 提取token进行验证String token = extractToken(request);if (validateToken(token)) {attributes.put("user", getUserFromToken(token));return true;}return false;
}

7.2 消息持久化

@Service
public class MessageService {public void saveMessage(Message message) {// 将消息保存到数据库// 可结合Redis实现消息缓存}public List<Message> getHistoryMessages(String userId) {// 获取历史消息return messageRepository.findByToUserOrFromUser(userId);}
}
http://www.dtcms.com/a/557037.html

相关文章:

  • Go高并发在企业级项目中的实战应用:数据库访问与GIN+GORM深度实践
  • 在网站写小说怎么做封面产品宣传册设计与制作
  • AI学习和研究——环境部署
  • ubuntu中ssh连接root用户
  • (146页PPT)某大型汽车集团企业数字化转型数智化战略规划设计方案(附下载方式)
  • 【Koa.js】 第十课:RESTful API 设计
  • 网站想换个风格怎么做打开网站建设中是什么意思
  • 【26】OpenCV C++实战篇——opencv中 .at<uchar>() 和.ptr<uchar>() 使用方法的区别
  • 2025年10月AGI月评|OmniNWM/X-VLA/DreamOmni2等6大开源项目:自动驾驶、机器人、文档智能的“技术底座”全解析
  • AI训练新纪元:强化学习与LLM深度融合,ChatGPT背后的革命性突破
  • Hudi、Iceberg、Delta Lake、Paimon四种数据湖的建表核心语法
  • 【高阶数据结构】红黑树
  • 许昌网站制作公司百度指数数据分析平台入口
  • 【笔记】解决 ComfyUI 安装 comfy-mtb 节点后 “Face restoration models not found.” 报错
  • 简洁企业网站模板卖水果网站模板
  • Nginx缓存机制优化:FastCGI缓存与浏览器缓存
  • 襄阳万家灯火网站建设爱用建站平台
  • 3.2.2.SpringMVC简介
  • 帝国cms影视网站模板网站app的区别
  • Rust 结构体方法(Methods):为数据附加行为
  • Android Cursor AI代码编辑器
  • git add 一条命令太长换行
  • 数据仓库与传统数据库开发工具架构差异:Web 架构 vs 客户端工具
  • 百度网站快速排名公司营销策略ppt模板
  • 外骨骼机器人:下肢助力走路,减负 30% 的硬核机械魔法
  • Linux基础I/O-打开新世界的大门:文件描述符的“分身术”与高级重定向
  • 用Python来学微积分25-微积分中的函数奥秘:单调性、极值与最值
  • 免费信息网站排名做动画视频的网站有哪些
  • 从零搭建多子网 DHCP 服务:CentOS 双网卡多作用域实战与原理解析
  • 再议c语言的直接访问和间接访问