# 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進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。