# Spring Boot 中如何使用WebSocket ## 1. WebSocket 简介 ### 1.1 什么是WebSocket WebSocket是一种在单个TCP连接上进行全双工通信的协议,它允许服务端主动向客户端推送数据。与传统的HTTP请求-响应模式不同,WebSocket提供了持久化的连接,使得客户端和服务器之间的数据交换变得更加高效。 ### 1.2 WebSocket与HTTP的区别 | 特性 | WebSocket | HTTP | |---------------|-------------------------------|--------------------------| | 通信模式 | 全双工 | 半双工(请求-响应) | | 连接持续时间 | 持久化 | 短暂(每次请求后关闭) | | 头部开销 | 初始握手后基本无头部 | 每次请求都携带完整头部 | | 适用场景 | 实时应用(聊天、游戏等) | 传统网页内容获取 | ### 1.3 WebSocket的应用场景 - 实时聊天应用 - 在线协作编辑工具 - 多人在线游戏 - 股票行情实时推送 - IoT设备状态监控 ## 2. Spring Boot集成WebSocket ### 2.1 添加依赖 在`pom.xml`中添加以下依赖: ```xml <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-websocket</artifactId> </dependency>
创建配置类启用WebSocket支持:
@Configuration @EnableWebSocket public class WebSocketConfig implements WebSocketConfigurer { @Override public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) { registry.addHandler(myHandler(), "/ws") .setAllowedOrigins("*"); } @Bean public WebSocketHandler myHandler() { return new MyWebSocketHandler(); } }
创建自定义处理器处理消息:
public class MyWebSocketHandler extends TextWebSocketHandler { private static final List<WebSocketSession> sessions = new CopyOnWriteArrayList<>(); @Override public void afterConnectionEstablished(WebSocketSession session) { sessions.add(session); System.out.println("New connection: " + session.getId()); } @Override protected void handleTextMessage(WebSocketSession session, TextMessage message) { String payload = message.getPayload(); System.out.println("Received: " + payload); // 广播消息给所有客户端 for (WebSocketSession s : sessions) { try { s.sendMessage(new TextMessage("Echo: " + payload)); } catch (IOException e) { e.printStackTrace(); } } } @Override public void afterConnectionClosed(WebSocketSession session, CloseStatus status) { sessions.remove(session); System.out.println("Connection closed: " + session.getId()); } }
添加STOMP子协议支持:
@Configuration @EnableWebSocketMessageBroker public class WebSocketStompConfig implements WebSocketMessageBrokerConfigurer { @Override public void configureMessageBroker(MessageBrokerRegistry config) { config.enableSimpleBroker("/topic"); config.setApplicationDestinationPrefixes("/app"); } @Override public void registerStompEndpoints(StompEndpointRegistry registry) { registry.addEndpoint("/ws-stomp") .setAllowedOrigins("*") .withSockJS(); } }
当浏览器不支持WebSocket时,SockJS会自动降级使用其他技术:
registry.addEndpoint("/ws") .withSockJS();
实现握手拦截器:
public class AuthHandshakeInterceptor implements HandshakeInterceptor { @Override public boolean beforeHandshake(ServerHttpRequest request, ServerHttpResponse response, WebSocketHandler wsHandler, Map<String, Object> attributes) { // 验证逻辑 return true; // 返回false拒绝连接 } @Override public void afterHandshake(ServerHttpRequest request, ServerHttpResponse response, WebSocketHandler wsHandler, Exception exception) { } }
注册拦截器:
registry.addHandler(myHandler(), "/ws") .addInterceptors(new AuthHandshakeInterceptor());
const socket = new WebSocket('ws://localhost:8080/ws'); socket.onopen = () => { console.log('Connected'); socket.send('Hello Server!'); }; socket.onmessage = (event) => { console.log('Received: ' + event.data); }; socket.onclose = () => { console.log('Disconnected'); };
使用WebSocketClient
:
WebSocketClient client = new StandardWebSocketClient(); WebSocketSession session = client.doHandshake( new WebSocketHandler() { /*...*/ }, "ws://localhost:8080/ws" ).get();
registry.setMessageSizeLimit(128 * 1024); // 128KB
解决方案: - 使用Redis广播消息
@Configuration @EnableRedisRepositories public class RedisConfig { @Bean public RedisMessageListenerContainer redisContainer(RedisConnectionFactory factory) { RedisMessageListenerContainer container = new RedisMessageListenerContainer(); container.setConnectionFactory(factory); return container; } }
@Controller public class ChatController { @MessageMapping("/chat.send") @SendTo("/topic/public") public ChatMessage sendMessage(@Payload ChatMessage message) { return message; } @MessageMapping("/chat.join") @SendTo("/topic/public") public ChatMessage join(@Payload ChatMessage message, SimpMessageHeaderAccessor headerAccessor) { headerAccessor.getSessionAttributes().put("username", message.getSender()); return message; } }
<div id="chat"> <input type="text" id="message" /> <button onclick="send()">Send</button> <div id="messages"></div> </div> <script> const stompClient = new StompJs.Client({ brokerURL: 'ws://localhost:8080/ws-stomp' }); stompClient.onConnect = () => { stompClient.subscribe('/topic/public', (message) => { showMessage(JSON.parse(message.body)); }); }; function send() { const message = document.getElementById('message').value; stompClient.publish({ destination: '/app/chat.send', body: JSON.stringify({'content': message}) }); } </script>
@SpringBootTest class WebSocketTests { @Autowired private WebSocketHandler handler; @Test void testMessageHandling() throws Exception { TestWebSocketSession session = new TestWebSocketSession(); handler.afterConnectionEstablished(session); TextMessage message = new TextMessage("test"); handler.handleMessage(session, message); assertEquals(1, session.getSentMessages().size()); } }
暴露WebSocket统计信息:
@Bean public WebSocketMetrics webSocketMetrics() { return new WebSocketMetrics(); }
spring-boot-starter-websocket
简化集成附录:完整配置示例
@Configuration @EnableWebSocketMessageBroker public class FullWebSocketConfig implements WebSocketMessageBrokerConfigurer { @Override public void configureMessageBroker(MessageBrokerRegistry registry) { registry.enableStompBrokerRelay("/topic", "/queue") .setRelayHost("rabbitmq-host") .setRelayPort(61613); registry.setApplicationDestinationPrefixes("/app"); } @Override public void registerStompEndpoints(StompEndpointRegistry registry) { registry.addEndpoint("/ws-full") .setHandshakeHandler(new CustomHandshakeHandler()) .addInterceptors(new AuthInterceptor()) .setAllowedOrigins("*") .withSockJS(); } @Override public void configureWebSocketTransport(WebSocketTransportRegistration registry) { registry.setMessageSizeLimit(512 * 1024) .setSendTimeLimit(10000) .setSendBufferSizeLimit(1024 * 1024); } }
”`
免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。