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

WebSocket详细教程 - SpringBoot实战指南

目录

1. 什么是WebSocket

1.1 基本概念

1.2 形象比喻

1.3 核心特点

2. WebSocket vs HTTP对比

2.1 通信方式对比

2.2 详细对比表

3. WebSocket协议原理

3.1 连接建立过程

步骤1:客户端发起握手请求

步骤2:服务器响应握手

步骤3:协议升级完成

3.2 数据帧格式

4. SpringBoot中的WebSocket

4.1 添加依赖

4.2 SpringBoot中的两种实现方式

5. 基础实现示例

5.1 原生WebSocket实现

5.1.1 WebSocket配置类

5.1.2 WebSocket处理器

5.1.3 前端JavaScript代码

6. STOMP协议详解

6.1 什么是STOMP

6.2 STOMP的优势

6.3 STOMP配置

6.3.1 配置类

6.3.2 消息控制器

6.3.3 消息实体类

6.3.4 WebSocket事件监听器

6.3.5 前端STOMP客户端

7. 完整项目实战

7.1 项目结构

7.2 主要业务逻辑

7.2.1 聊天服务类

7.2.2 增强的聊天控制器

8. 高级特性

8.1 身份验证和授权

8.1.1 WebSocket安全配置

8.2 消息持久化

8.2.1 消息实体

8.2.2 消息仓库

8.3 集群支持

8.3.1 Redis消息代理配置

9. 最佳实践

9.1 连接管理

9.1.1 连接池管理

9.2 错误处理和重连机制

9.2.1 客户端重连逻辑

9.3 性能优化

9.3.1 消息限流

9.3.2 消息压缩

10. 常见问题与解决方案

10.1 连接问题

问题1:WebSocket连接被防火墙阻止

问题2:跨域问题

10.2 性能问题

问题1:内存泄漏

问题2:消息堆积

10.3 安全问题

问题1:未授权访问

问题2:消息内容过滤

🎯 总结

学习建议

适用场景


1. 什么是WebSocket

1.1 基本概念

WebSocket是一种网络通信协议,提供了全双工通信能力。简单来说:

  • 传统HTTP:客户端问一次,服务器答一次(单向请求-响应)
  • WebSocket:客户端和服务器可以随时互相发送消息(双向实时通信)

1.2 形象比喻

想象一下:

  • HTTP就像写信:你写信给朋友,朋友收到后回信给你,一来一回
  • WebSocket就像打电话:连接建立后,双方都可以随时说话,实时交流

1.3 核心特点

特点说明
持久连接一次握手,持续通信
全双工双方都可主动发送消息
低延迟无需重复建立连接
轻量级数据传输开销小

2. WebSocket vs HTTP对比

2.1 通信方式对比

HTTP通信流程:
客户端 ──请求──> 服务器
客户端 <──响应── 服务器
客户端 ──请求──> 服务器  (新的连接)
客户端 <──响应── 服务器WebSocket通信流程:
客户端 ──握手请求──> 服务器
客户端 <──握手响应── 服务器
客户端 <──────────> 服务器  (持续双向通信)

2.2 详细对比表

对比项HTTPWebSocket
连接性质无状态、短连接有状态、长连接
通信方向单向(请求-响应)双向(全双工)
服务器推送不支持原生支持
协议开销每次请求都有完整HTTP头握手后开销极小
实时性差(需要轮询)优秀(即时推送)
适用场景一般Web应用实时应用(聊天、游戏等)

3. WebSocket协议原理

3.1 连接建立过程

步骤1:客户端发起握手请求
GET /chat HTTP/1.1
Host: example.com
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==
Sec-WebSocket-Version: 13
步骤2:服务器响应握手
HTTP/1.1 101 Switching Protocols
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=
步骤3:协议升级完成

连接从HTTP协议升级为WebSocket协议,开始全双工通信。

3.2 数据帧格式

WebSocket使用帧(Frame)来传输数据:

 0                   1                   2                   30 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-------+-+-------------+-------------------------------+
|F|R|R|R| opcode|M| Payload len |    Extended payload length    |
|I|S|S|S|  (4)  |A|     (7)     |             (16/64)           |
|N|V|V|V|       |S|             |   (if payload len==126/127)   |
| |1|2|3|       |K|             |                               |
+-+-+-+-+-------+-+-------------+ - - - - - - - - - - - - - - - +

4. SpringBoot中的WebSocket

4.1 添加依赖

<dependencies><!-- SpringBoot Web Starter --><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><!-- SpringBoot WebSocket Starter --><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-websocket</artifactId></dependency>
</dependencies>

4.2 SpringBoot中的两种实现方式

方式特点适用场景
原生WebSocket底层API,灵活度高简单点对点通信
STOMP协议高级消息协议,功能丰富复杂消息系统

5. 基础实现示例

5.1 原生WebSocket实现

5.1.1 WebSocket配置类
@Configuration
@EnableWebSocket
public class WebSocketConfig implements WebSocketConfigurer {@Overridepublic void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {// 注册WebSocket处理器registry.addHandler(new MyWebSocketHandler(), "/websocket").setAllowedOrigins("*"); // 允许跨域}
}
5.1.2 WebSocket处理器
@Component
public class MyWebSocketHandler extends TextWebSocketHandler {// 存储所有WebSocket会话private static final Set<WebSocketSession> sessions = Collections.synchronizedSet(new HashSet<>());/*** 连接建立成功调用的方法*/@Overridepublic void afterConnectionEstablished(WebSocketSession session) throws Exception {sessions.add(session);System.out.println("连接建立:" + session.getId());// 向新连接的客户端发送欢迎消息session.sendMessage(new TextMessage("欢迎连接WebSocket服务器!"));}/*** 接收到消息时调用的方法*/@Overrideprotected void handleTextMessage(WebSocketSession session, TextMessage message) throws Exception {String payload = message.getPayload();System.out.println("收到消息:" + payload);// 广播消息给所有连接的客户端broadcastMessage("用户说:" + payload);}/*** 连接关闭后调用的方法*/@Overridepublic void afterConnectionClosed(WebSocketSession session, CloseStatus status) throws Exception {sessions.remove(session);System.out.println("连接关闭:" + session.getId());}/*** 传输错误时调用的方法*/@Overridepublic void handleTransportError(WebSocketSession session, Throwable exception) throws Exception {System.out.println("传输错误:" + exception.getMessage());sessions.remove(session);}/*** 广播消息给所有客户端*/private void broadcastMessage(String message) {synchronized (sessions) {for (WebSocketSession session : sessions) {try {if (session.isOpen()) {session.sendMessage(new TextMessage(message));}} catch (Exception e) {e.printStackTrace();}}}}
}
5.1.3 前端JavaScript代码
<!DOCTYPE html>
<html>
<head><title>WebSocket测试</title>
</head>
<body><div><input type="text" id="messageInput" placeholder="输入消息..."><button onclick="sendMessage()">发送</button><button onclick="connect()">连接</button><button onclick="disconnect()">断开</button></div><div id="messages"></div><script>let socket = null;function connect() {socket = new WebSocket('ws://localhost:8080/websocket');socket.onopen = function(event) {console.log('WebSocket连接已建立');addMessage('已连接到服务器');};socket.onmessage = function(event) {console.log('收到消息:', event.data);addMessage('服务器:' + event.data);};socket.onclose = function(event) {console.log('WebSocket连接已关闭');addMessage('连接已断开');};socket.onerror = function(error) {console.log('WebSocket错误:', error);addMessage('连接错误:' + error);};}function sendMessage() {const input = document.getElementById('messageInput');if (socket && socket.readyState === WebSocket.OPEN) {socket.send(input.value);addMessage('我:' + input.value);input.value = '';} else {alert('请先连接WebSocket');}}function disconnect() {if (socket) {socket.close();}}function addMessage(message) {const messages = document.getElementById('messages');cons
http://www.dtcms.com/a/269967.html

相关文章:

  • EPLAN 电气制图(四):EPLAN 总电源电路设计知识详解
  • mit6.5840-lab3-3D-SnapShot-25Summer
  • 常见前端开发问题的解决办法
  • 深度学习——神经网络1
  • JK触发器Multisim电路仿真——硬件工程师笔记
  • HMI安全设计规范:ISO 26262合规的功能安全实现路径
  • python2.7/lib-dynload/_ssl.so: undefined symbol: sk_pop_free
  • 查询依赖冲突工具maven Helper
  • 常见的网络攻击方式及防御措施
  • 人工智能与人工智障———仙盟创梦IDE
  • Go HTTP 调用(上)
  • LeetCode 1248.统计优美子数组
  • cocos2dx3.x项目升级到xcode15以上的iconv与duplicate symbols报错问题
  • 云原生时代的日志管理:ELK、Loki、Fluentd 如何选型?
  • C++11 算法详解:std::copy_if 与 std::copy_n
  • UVC(USB Video Class,USB 视频类)协议
  • 代码详细注释:ARM-Linux字符设备驱动开发案例:LCD汉字输出改进建议开发板断电重启还能显示汉字,显示汉字位置自定义
  • 高版本的MacOS如何降级?
  • 数据库|达梦DM数据库配置实例步骤
  • npm 包 scheduler 介绍
  • 黑马点评系列问题之P37商户点评缓存作业,用了string和list两种方法,可以直接复制粘贴
  • K8s-配置管理
  • 【web安全】SQLMap 参数深度解析:--risk 与 --level 详解
  • linux网络编程之IO多路复用模型
  • 车载以太网-TC8测试-UT(Upper Tester)
  • Redis 缓存进阶篇,缓存真实数据和缓存文件指针最佳实现?如何选择?
  • 2025年微软mos备考攻略-穷鬼版
  • 3.2 ASPICE的项目监控
  • 内网服务器怎么设置公网远程访问? windows桌面连接和Linux自带SSH外网异地跨网用完整步骤教程
  • K8s——配置管理(2)