【Spring WebSocket详解】Spring WebSocket从入门到实战
一、WebSocket协议基础
1.1 Websocket概念
1.2 WebSocket握手流程:
二、Spring WebSocket技术详解
2.1 Spring WebSocket概述
2.2 STOMP协议支持
三、 STOMP协议实现简单通信系统
3.1 pom.xml文件
3.2 配置STOMP代理
3.3 使用@MessageMapping的Controller
3.4 前端连接STOMP(Vue2)
四、Spring WebSocket构建简单实时通信系统
4.1 pom.xml文件
4.2 核心配置类
4.3 消息处理器类
4.4 客户端测试代码(Vue2)
五、扩展与其他
在现代Web应用中,实时通信已成为基本需求。无论是聊天应用、实时通知系统还是在线协作工具,都需要服务器能够主动向客户端推送消息。本文将详细介绍如何使用Spring WebSocket构建一个高效的实时通信系统。
一、WebSocket协议基础
1.1 Websocket概念
WebSocket是一种在单个TCP连接上进行全双工通信的协议,解决了HTTP协议的半双工和轮询效率低的问题。其核心特点:
全双工:客户端和服务端可同时发送数据。
持久连接:一次握手后保持长连接。
低延迟:无需频繁建立连接。
1.2 WebSocket握手流程:
客户端发起HTTP请求:包含Upgrade:websocket头。
服务端响应101状态码:表示协议切换成功。
数据帧传输:后续通信通过二进制帧(Frame)进行。
握手请求示例:
GET /chat HTTP/1.1
Host: server.example.com
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Key: x3JJHMbDL1EzLkh9GBhXDw==
Sec-WebSocket-Version: 13
握手响应示例:
HTTP/1.1 101 Switching Protocols
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Accept: HSmrc0sMlYUkAGmm5OPpG2HaGWk=
二、Spring WebSocket技术详解
2.1 Spring WebSocket概述
Spring WebSocket是Spring框架对WebSocket协议的实现,提供了更高层次的抽象,与Spring生态系统无缝集成。
核心组件:
WebSocketHandler:处理WebSocket消息的接口
public class MyRawHandler implements WebSocketHandler {// 处理消息@Overridepublic void handleMessage(WebSocketSession session, WebSocketMessage<?> message) {if (message instanceof TextMessage) {handleTextMessage(session, (TextMessage) message);}else if (message instanceof BinaryMessage) {handleBinaryMessage(session, (BinaryMessage) message);}}// 必须实现的空方法(适配器模式)@Override public void afterConnectionEstablished(WebSocketSession session) {}@Override public void handleTransportError(WebSocketSession session, Throwable exception) {}@Override public void afterConnectionClosed(WebSocketSession session, CloseStatus closeStatus) {}@Override public boolean supportsPartialMessages() { return false; }
}
WebSocketSession:代表一个WebSocket连接
// 广播消息到主题
template.convertAndSend("/topic/public", chatMessage);// 发送给特定用户
template.convertAndSendToUser(userId, "/queue/private", message);
SimpMessagingTemplate:用于发送消息的工具类
// 广播消息到主题
template.convertAndSend("/topic/public", chatMessage);// 发送给特定用户
template.convertAndSendToUser(userId, "/queue/private", message);
@MessageMapping:是 Spring STOMP协议的一部分,用于定义消息的接收端点(类似HTTP的 @RequestMapping)
2.2 STOMP协议支持
Spring WebSocket默认支持STOMP(Simple Text Oriented Messaging Protocol)子协议,提供了更高级的消息模式:
目的地(Destination)概念:
/app:应用前缀(由@MessageMapping处理)
/topic:广播目的地
/queue:点对点队列
/user:用户专属队列(自动转换)
@Override
public void configureMessageBroker(MessageBrokerRegistry config) {config.setApplicationDestinationPrefixes("/app");config.enableSimpleBroker("/topic", "/queue");config.setUserDestinationPrefix("/user");
}
订阅/发布模式:
前端订阅:
stompClient.subscribe('/topic/public', (message) => {console.log('收到广播:', message.body);
});stompClient.subscribe('/user/queue/private', (message) => {console.log('收到私信:', message.body);
});
后端发布:
@MessageMapping("/chat")
@SendTo("/topic/public")
public ChatMessage broadcast(ChatMessage message) {return message; // 自动发送到/topic/public
}
消息确认ACK机制:
服务端配置:
@Configuration
@EnableWebSocketMessageBroker
public class WebSocketConfig implements WebSocketMessageBrokerConfigurer {@Overridepublic void configureClientInboundChannel(ChannelRegistration registration) {registration.interceptors(new ChannelInterceptor() {@Overridepublic Message<?> preSend(Message<?> message, MessageChannel channel) {// 消息到达前的处理return message;}});}
}
客户端ACK示例:
stompClient.subscribe('/topic/orders', (message) => {processOrder(message.body);message.acknowledge(); // 显式确认
}, { 'ack': 'client' });
三、 STOMP协议实现简单通信系统
3.1 pom.xml文件
<dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-websocket</artifactId><version>2.7.0</version></dependency>
</dependencies>
3.2 配置STOMP代理
@Configuration
@EnableWebSocketMessageBroker
public class WebSocketStompConfig implements WebSocketMessageBrokerConfigurer {@Overridepublic void configureMessageBroker(MessageBrokerRegistry registry) {registry.enableSimpleBroker("/topic"); // 客户端订阅前缀registry.setApplicationDestinationPrefixes("/app"); // 服务端接收前缀}@Overridepublic void registerStompEndpoints(StompEndpointRegistry registry) {registry.addEndpoint("/ws-stomp").withSockJS(); // 连接端点}
}
3.3 使用@MessageMapping的Controller
@Controller
public class StompMessageController {// 处理发送到 "/app/chat" 的消息@MessageMapping("/chat") @SendTo("/topic/messages") // 自动广播到所有订阅"/topic/messages"的客户端public String handleChat(String message) {return "回复: " + message;}
}
3.4 前端连接STOMP(Vue2)
const socket = new SockJS('/ws-stomp');
const stompClient = Stomp.over(socket);stompClient.connect({}, () => {// 订阅消息stompClient.subscribe('/topic/messages', (response) => {console.log("收到广播: " + response.body);});// 发送消息stompClient.send("/app/chat", {}, "Hello STOMP!");
});
四、Spring WebSocket构建简单实时通信系统
4.1 pom.xml文件
<dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-websocket</artifactId><version>2.7.0</version></dependency>
</dependencies>
4.2 核心配置类
/*** WebSocket 配置类,用于配置 WebSocket 相关参数和处理器*/
@Configuration
@EnableWebSocket
public class WebSocketConfig implements WebSocketConfigurer {@Autowired // 自动注入自定义的 WebSocket 消息处理器private BroadcastWebSocketHandler handler;@Overridepublic void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {// 添加自定义的 WebSocket 处理器,并指定 WebSocket 连接路径registry.addHandler(handler, "/ws")// 设置允许跨域访问,* 表示允许所有来源.setAllowedOrigins("*");// 可以继续添加其他配置,例如:// .withSockJS() // 如果需要支持 SockJS 可以添加此项}
}
4.3 消息处理器类
import org.springframework.web.socket.*;
import org.springframework.web.socket.handler.TextWebSocketHandler;
import java.util.concurrent.CopyOnWriteArrayList;public class CustomWebSocketHandler extends TextWebSocketHandler {// 线程安全的连接集合private final List<WebSocketSession> sessions = new CopyOnWriteArrayList<>();// 1. 连接建立时触发@Overridepublic void afterConnectionEstablished(WebSocketSession session) {sessions.add(session);log.info("新连接: ID={}, URI={}", session.getId(), session.getUri());}// 2. 接收文本消息时触发@Overrideprotected void handleTextMessage(WebSocketSession session, TextMessage message) {String payload = message.getPayload();log.info("收到消息: 来自{} 内容={}", session.getId(), payload);// 使用Jackson将JSON字符串转换为User对象(假如是user对象)ObjectMapper objectMapper = new ObjectMapper();User user = objectMapper.readValue(payload, User.class);// 业务处理逻辑processMessage(session, payload);}// 3. 连接关闭时触发@Overridepublic void afterConnectionClosed(WebSocketSession session, CloseStatus status) {sessions.remove(session);log.info("连接关闭: ID={}, 原因={}", session.getId(), status.getReason());}// 4. 传输错误处理@Overridepublic void handleTransportError(WebSocketSession session, Throwable exception) {log.error("传输错误: " + session.getId(), exception);}
}
4.4 前端连接代码(Vue2)
// Client1
const ws1 = new WebSocket('ws://localhost:8080/ws');
ws1.onmessage = e => console.log('客户端1收到:', e.data);
ws1.send("Hello from Client1");// Client2
const ws2 = new WebSocket('ws://localhost:8080/ws');
ws2.onmessage = e => console.log('客户端2收到:', e.data);
ws2.send("Hello from Client2");
五、扩展与其他
如果你希望使用Netty实现性能更好的通信系统,请参考我的其他文章:
通过以上实战指南,您可以通过编写指定的handler类来构建一个稳定、高效、可扩展的Spring WebSocket实时通信系统,满足大多数实时通信场景的需求,如果有其他问题欢迎评论区留言讨论。