本文主要是介绍Spring Boot中WebSocket常用使用方法详解,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!
《SpringBoot中WebSocket常用使用方法详解》本文从WebSocket的基础概念出发,详细介绍了SpringBoot集成WebSocket的步骤,并重点讲解了常用的使用方法,包括简单消...
在实时性要求较高的应用场景,如在线聊天、实时数据监控、股票行情推送等,传统的HTTP协议由于其请求-响应的模式,无法高效实现服务器与客户端之间的双向实时通信。而WebSocket协议的出现解决了这一难题,它允许在单个TCP连接上进行全双工通信,使得服务器和客户端可以随时主动发送消息。Spring Boot对WebSocket提供了良好的支持,极大地简化了开发流程。本文将从入门到精通,详细介绍Spring Boot中WebSocket的常用使用方法。
一、WebSocket基础概念
1.1 什么是WebSocket
WebSocket是一种网络通信协议,于2011年被IETF定为标准RFC 6455,并被html5所支持 。与HTTP协议不同,WebSocket在建立连接后,通信双方可以随时主动发送和接收数据,无需像HTTP那样每次通信都要建立新的连接,从而减少了开销,提高了实时性。
1.2 WebSocket与HTTP的区别
特性 | HTTP | WebSocket |
---|---|---|
通信模式 | 客户端发起请求,服务器响应(单向) | 全双工通信(双向) |
连接方式 | 每次请求都需建立新连接 | 一次握手建立持久连接 |
数据格式 | 通常为文本(jsON、XML等) | 支持文本和二进制数据 |
应用场景 | 适用于一般的Web页面请求 | 适用于实时性要求高的场景 |
二、Spring Boot集成WebSocket
2.1 添加依赖
在Spring Boot项目的pom.xml
文件中添加WebSocket依赖:
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-websocket</artifactId> </dependency>
如果使用Gradle,在build.gradle
中添加:
implementation 'org.springframework.boot:spring-boot-starter-websocket'
2.2 配置WebSocket
创建一个配置类,用于注册WebSocket处理程序和配置消息代理:
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.socket.config.annotation.EnableWebSocketMessageBroker;
import org.springframework.web.socket.config.annotation.StompEndpointRegistry;
import org.springframework.web.socket.config.annotation.WebSocketMessageBrokerConfigurer;
import org.springframework.web.socket.server.standard.ServletServerContainerFactoryBean;
@Configuration
@EnableWebSocketMessageBroker
public class WebSocketConfig implements WebSocketMessaChina编程geBrokerConfigurer {
@Override
public void configureMessageBroker(StompBrokerRelayRegistration config) {
config.setApplicationDestinationPrefixes("/app");
config.setDestinationPrefixes("/topic");
}
@Override
public void registerStompEndpoints(StompEndpointRegistry registry) {
registry.addEndpoint("/websocket-endpoint").withSockJS();
}
@Bean
public ServletServerContainerFactoryBean createWebSocketContainer() {
ServletServerContainerFactoryBean container = new ServletServerContainerFactoryBean();
container.setMaxTextMessageBufferSize(65536);
container.setMaxBinaryMessageBufferSize(65536);
return container;
}
}
在上述代码中:
@EnableWebSocketMessageBroker
注解启用WebSocket消息代理。configureMessageBroker
方法配置消息代理的前缀,/app
用于应用程序发送消息的目的地前缀,/topic
用于服务器发送消息的目的地前缀。registerStompEndpoints
方法注册WebSocket端点,addEndpoint
方法指定端点的路径,withSockJS
表示启用SockJS支持,以提供对不支持WebSocket浏览器的兼容。crjavascripteateWebSocketContainer
方法配置WebSocket容器的参数,如消息缓冲区大小。
三、WebSocket常用使用方法
3.1 简单消息收发
3.1.1 创建消息实体类
public class ChatMessage { private String sender; private String content; private MessageType type; // 省略构造函数、Getter和Setter方法 public enum MessageType { CHAT, JOIN, LEAVE } }
ChatMessage
类用于封装聊天消息,包含发送者、消息内容和消息类型(聊天、加入、离开)。
3.1.2 创建消息处理类
import org.springframework.messaging.handler.annotation.MessageMapping; import org.springframework.messaging.handler.annotation.SendTo; import org.springframework.stereotype.Controller; @Controller public class ChatController { @MessageMapping("/chat.send") @SendTo("/topic/public") public ChatMessage sendMessage(ChatMessage chatMessage) { return chatMessage; } @MessageMapping("/chat.join") @SendTo("/topic/public") public ChatMessage joinChat(ChatMessage chatMessage) { chatMessage.setType(ChatMessage.MessageType.JOIN); return chatMessage; } }
在上述代码中:
@MessageMapping
注解用于映射客户端发送的消息路径,如"/chat.send"
和"/chat.join"
。@SendTo
注解指定消息发送的目的地,这里将消息发送到"/topic/public"
,所有订阅该主题的客户端都能接收到消息。sendMessage
方法处理聊天消息的发送,joinChat
方法处理用户加入聊天的消息。
3.1.3 前端页面实现
<!DOCTYPE html> <html lang="zh"> <head> <meta charset="UTF-8"> <title>WebSocket Chat</title> <script src="https://cdnjs.cloudflare.com/AJAX/libs/sockjs-client/1.5.1/sockjs.min.js"></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/stompjs/2.3.3/stomp.min.js"></script> </head> <body> <input type="text" id="username" placeholder="用户名"> <button onclick="connect()">连接</button> <div id="chat-window"></div> <input type="text" id="message" placeholder="输入消息"> <button onclick="sendMessage()">发送</button> <script> let socket = new SockJS('/websocket-endpoint'); let stompClient = Stomp.over(socket); function connect() { let username = document.getElementById('username').value; stompClient.connect({}, function (frame) { console.log('Connected: ' + frame); stompClient.subscribe('/topic/public', function (message) { let chatWindow = document.getElementById('chat-window'); let msg = JSON.parse(message.body); if (msg.type === 'JOIN') { chatWindow.innerHTML += msg.sender + " 加入了聊天 "; } else { chatWindow.innerHTML += msg.sender + ": " + msg.content + " "; } }); let joinMessage = { sender: username, content: '', type: 'JOIN' }; stompClient.send("/app/chat.join", {}, JSON.stringify(joinMessage)); }); } function sendMessage() { let message = document.getElementById('message').value; let username = document.getElementById('username').value; let chatMessage = { sender: username, content: message, type: 'CHAT' }; stompClient.send("/app/chat.send", {}, JSON.stringify(chatMessage)); } </script> </body> </html>
前端页面通过SockJS和StompJS库与后端建立WebSocket连接,实现消息的发送和接收。
3.2 点对点消息发送
有时候需要实现一对一的消息发送,而不是广播给所有客户端。可以通过在@SendTo
中指定具体的用户目的地来实现。
3.2.1 配置用户目的地前缀
在WebSocketConfig
类中添加用户目的地前缀配置:
@Override public void configureMessageBroker(StompBrokerRelayRegistration config) { config.setApplicationDestinationPrefixes("/app"); config.setDestinationPrefixes("/topic", "/user"); config.setUserDestinationPrefix("/user"); }
这里添加了/user
作为用户目的地前缀。
3.2.2 修改消息处理类
import org.springframework.messaging.handler.annotation.MessageMapping; import org.springframework.messaging.handler.annotation.SendTo; import org.springframework.messaging.simp.SimpMessageHeaderAccessor; import org.springframework.stereotype.Controller; @Controller public class PrivateChatController { @MessageMapping("/chat.private") public void sendPrivateMessage(SimpMessageHeaderAccessor headerAccessor, ChatMessage chatMessage) { String recipient = chatMessage.getRecipient(); headerAccessor.getSessionAttributes().put("username", chatMessage.getSender()); this.stompMessagingTemplate.convertAndSendToUser(recipient, "/private", chatMessage); } }
在上述代码中:
@MessageMapping("/chat.private")
映射处理点对点消息的路径。SimpMessageHeaderAccessor
用于获取和设置消息头信息。stompMessagingTemplate.convertAndSendToUser
方法将消息发送到指定用户的私有目的地。
3.2.3 前端实现点对点消息发送
function sendPrivateMessage() { let message = document.getElementById('message').value; let username = document.getElementById('username').value; let recipient = document.getElementById('recipient').value; let chatMessage = { sender: username, recipient: recipient, content: message, type: 'CHAT' }; stompClient.send("/app/chat.private", {}, JSON.stringify(chatMessage)); }
前端添加输入接收者的文本框,并在发送消息时指定接收者,实现点对点消息发送。
3.3 消息拦截与认证
在实际应用中,可能需要对WebSocket消息进行拦截和认证,确保只有合法用户才能进行通信。
3.3.1 创建消息拦截器
import org.springframework.messaging.Message; import org.springframework.messaging.MessageChannel; import org.springframework.messaging.simp.stomp.StompCommand; import org.springframework.messaging.simp.stomp.StompHeaderAccessor; import org.springframework.messaging.support.ChannelInterceptor; import org.springframework.messaging.support.MessageHeaderAccessor; import org.springframework.stereotype.Component; @Component public class WebSocketInterceptor implements ChannelInterceptor { @Override public Message<?> preSend(Message<?> message, MessageChannel channel) { StompHeaderAccessor accessor = MessageHeaderAccessor.getAccessor(message, StompHeaderAccessor.class); if (StompCommand.CONNECT.equals(accessor.getCommand())) { // 在这里进行认证逻辑,如检查Token等 String token = accessor.getFirstNativeHeader("Authorization"); if (token == null ||!isValidToken(token)) { throw new RuntimeException("认证失败"); } } return message; } private boolean isValidToken(String token) { // 实现具体的Token验证逻辑 return true; } }
上述代码创建了一个WebSocketInterceptor
拦截器,在preSend
方法中对连接请求进行认证,检查请求头中的Authorization
Token是否有效。
3.3.2 注册拦截器
在WebSocketConfig
类中注册拦截器:
@Configuration @EnableWebSocketMessageBroker public class WebSocketConfig implements WebSocketMessageBrokerConfigurer { @Override public void configureClientInboundChannel(ChannelRegistration registration) { registration.interceptors(new WebSocketInterceptor()); } // 其他配置方法... }
通过configureClientInboundChannel
方法将拦截器注册到客户端入站通道,对所有进入的消息进行拦截处理。
四、不使用接口,基于注解的WebSocket实现
4.1 实现思路
在Spring Boot中,除了通过实现接口的方式处理WebSocket消息,还可以利用注解来简化开发过程。通过@ServerEndpoint
注解定义WebSocket端点,结合@OnOpen
、@OnMessage
、@OnClose
、@OnError
等注解,能够轻松实现对WebSocket连接生命周期的监听,以及接收和处理客户端发送的数据。
4.2 核心代码实现
首先,创建一个WebSocket处理类:
import Javax.websocket.*; import javax.websocket.server.PathParam; import javax.websocket.server.ServerEndpoint; import java.io.IOException; import java.util.concurrent.CopyOnWriteArraySet; @ServerEndpoint("/ws/{userId}") public class MyWebSocket { // 静态变量,用来记录当前在线连接数。应该把它设计成线程安全的。 private static int onlineCount = 0; // concurrent包的线程安全Set,用来存放每个客户端对应的MyWebSocket对象。 private static CopyOnWriteArraySet<MyWebSocket> webSocketSet = new CopyOnWriteArraySet<>(); // 与某个客户端的连接会话,需要通过它来给客户端发送数据 private Session session; // 接收userId private String userId; /** * 连接建立成功调用的方法 */ @OnOpen public void onOpen(Session session, @PathParam("userId") String userId) { this.session = session; this.userId = userId; webSocketSet.add(this); addOnlineCount(); System.out.println("有新连接加入!当前在线人数为" + getOnlineCount()); } /** * 连接关闭调用的方法 */ @OnClose public void onClose() { webSocketSet.remove(this); subOnlineCount(); System.out.println("有一连接关闭!当前在线人数为" + getOnlineCount()); } /** * 收到客户端消息后调用的方法 * * @param message 客户端发送过来的消息 */ @OnMessage public void onMessage(String message, Session session) { System.out.println("来自客户端" + userId + "的消息:" + message); // 群发消息 for (MyWebSocket item : webSocketSet) { try { item.sendMessage(message); } catch (IOException e) { e.printStackTrace(); } } } /** * 发生错误时调用 * * @param session * @param error */ @OnError public void onError(Session session, Throwable error) { System.out.println("发生错误"); error.printStackTrace(); } public void sendMessage(String message) throws IOException { this.session.getBasicRemote().sendText(message); } public static synchronized int getOnlineCount() { return onlineCount; } public static synchronized void addOnlineCount() { MyWebSocket.onlineCount++; } public static synchronized void subOnlineCount() { MyWebSocket.onlineCount--; } }
在上述代码中:
@ServerEndpoint("/ws/{userId}")
注解定义了WebSocket的访问端点,{userId}
为路径参数,用于标识不同的客户端。@OnOpen
注解的方法在连接建立时被调用,用于初始化连接相关信息,并将当前连接对象添加到在线连接集合中。@OnMessage
注解的方法在接收到客户端发送的消息时被调用,实现了消息的接收和群发功能。@OnClose
注解的方法在连接关闭时被调用,从在线连接集合中移除当前连接对象。@OnError
注解的方法在发生错误时被调用,用于处理异常情况。
4.3 前端页面适配
前端页面同样需要进行相应的修改,以连接基于注解实现的WebSocket端点:
<!DOCTYPE html> <html lang="zh"> <head> <meta charset="UTF-8"> <title>基于注解的WebSocket Chat</title> </head> <body> <input type="text" id="userId" placeholder="用户ID"> <button onclick="connect()">连接</button> <div id="chat-window"></div> <input type="text" id="message" placeholder="输入消息"> <button onclick="sendMessage()">发送</button> <script> let socket; function connect() { let userId = document.getElementById('userId').value; socket = new WebSocket("ws://localhost:8080/ws/" + userId); 编程China编程 socket.onopen = function (event) { console.log("连接成功"); }; socket.onmessage = function (event) { let chatWindow = document.getElementById('chat-window'); chatWindow.innerHTML += "收到消息: " + event.data + " "; }; socket.onclose = function (event) { console.log("连接关闭"); }; socket.onerror = function (event) { console.log("连接错误"); }; } function sendMessage() { let message = document.getElementById('message').value; if (socket && socket.readyState === WebSocket.OPEN) { socket.send(message); document.getElementById('chat-window').innerHTML += "发送消息: " + message + " "; document.getElementById('message').value = ""; } else { alert("WebSocket连接未建立或已关闭"); } } </script> </body> </html>
4.4 配置WebSocket端点
还需要在Spring Boot中配置WebSocket支持,确保端点被正确注册:
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.web.socket.server.standard.ServletServerContainerFactoryBean; @Configuration public class WebSocketConfig { @Bean public ServletServerContainerFactoryBean createWebSocketContainer() { ServletServerContainerFactoryBean container = new ServletServerContainerFactoryBean(); container.setMaxTextMessageBufferSize(8192); container.setMaxBinaryMessageBufferSize(8192); return container; } }
这种基于注解的实现方式相比传统接口方式更加简洁直观,通过注解即可完成WebSocket连接的生命周期管理和消息处理。
五、应用场景拓展
5.1 实时数据推送
在股票交易、天气监测等场景中,服务器需要实时向客户端推送数据。可以结合定时任务实现:
@Service public class RealTimeDataService { @Autowired private SimpMessagingTemplate messagingTemplate; @Scheduled(fixedRate = 5000) // 每5秒执行一次 public void pushRealTimeData() { // 获取实时数据 StockData stockData = getStockData(); // 推送给订阅了实时股票信息的客户端 messagingTemplate.convertAndSend("/topic/stock", stockData); } private StockData getStockDajsta() { // 模拟获取股票数据 return new StockData("000001", "平安银行", 15.68, 0.23); } }
5.2 在线协作编辑
多个用户可以同时编辑同一个文档,实时看到彼此的操作:
@MessageMapping("/edit") @SendTo("/topic/document/{docId}") public EditOperation handleEdit(@DestinationVariable String docId, EditOperation operation) { // 处理编辑操作,更新文档 documentService.applyEdit(docId, operation); return operation; }
5.3 游戏实时对战
在在线游戏中,玩家的操作需要实时同步到其他玩家:
@MessageMapping("/game/{roomId}/move") public void handleGameMove(@DestinationVariable String roomId, MoveAction action) { // 更新游戏状态 gameService.updateGameState(roomId, action); // 广播给房间内的所有玩家 messagingTemplate.convertAndSend("/topic/game/" + roomId, action); }
六、性能优化与最佳实践
6.1 连接管理
- 使用连接池管理WebSocket连接,避免频繁创建和销毁连接
- 对长时间不活跃的连接进行心跳检测和自动关闭
- 限制单个客户端的连接数量,防止恶意连接
6.2 消息处理优化
- 对高频消息进行合并和批处理,减少网络开销
- 使用异步处理机制,避免阻塞主线程
- 对大消息进行分片传输,防止消息过大导致的性能问题
6.3 安全加固
- 使用SSL/TLS加密WebSocket连接,确保数据传输安全
- 实现严格的身份认证和权限控制
- 对客户端输入进行过滤和验证,防止XSS和SQL注入攻击
6.4 监控与告警
- 监控WebSocket连接数、消息吞吐量等指标
- 设置异常告警机制,及时发现和处理连接异常和性能问题
七、常见问题与解决方案
7.1 跨域问题
- 配置CORS允许WebSocket端点的跨域访问
registry.addEndpoint("/websocket-endpoint") .setAllowedOrigins("*") .withSockJS();
7.2 消息丢失问题
- 实现消息确认机制,确保消息可靠传递
- 使用持久化队列存储重要消息,防止服务器efbwKLeQ重启导致消息丢失
7.3 性能瓶颈
- 分析性能瓶颈点,针对性地进行优化
- 考虑使用分布式消息队列和集群部署提高系统吞吐量
八、总结
本文从WebSocket的基础概念出发,详细介绍了Spring Boot集成WebSocket的步骤,并重点讲解了常用的使用方法,包括简单消息收发、点对点消息发送、消息拦截与认证,以及不使用接口而是基于注解的WebSocket实现方式。同时,还拓展了WebSocket在不同场景下的应用,提供了性能优化建议和常见问题解决方案。
通过这些方法,开发者可以根据实际需求,灵活运用WebSocket在Spring Boot应用中实现高效的实时通信功能。在实际项目中,还可以结合更多的Spring Boot特性和业务逻辑,进一步扩展和优化WebSocket的应用,打造出更强大、更实用的实时应用程序。
以上补充内容完善了基于注解的WebSocket实现方案,并新增了应用场景拓展、性能优化等实用内容。如需进一步深入探讨某个主题,或需要其他补充,请随时告知。
到此这篇关于Spring Boot中WebSocket常用使用方法详解的文章就介绍到这了,更多相关springboot websocket使用内容请搜索China编程(www.chinasem.cn)以前的文章或继续浏览下面的相关文章希望大家以后多多支持China编程(www.chinasem.cn)!
这篇关于Spring Boot中WebSocket常用使用方法详解的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!