1. 概述
本快速教程將演示如何使用 Spring WebSockets 向特定會話或特定用户發送消息。
對於上述模塊的介紹,請參閲本文檔。
2. WebSocket 配置
首先,我們需要配置我們的消息代理和 WebSocket 應用端點:
@Configuration
@EnableWebSocketMessageBroker
public class WebSocketConfig
extends AbstractWebSocketMessageBrokerConfigurer {
@Override
public void configureMessageBroker(MessageBrokerRegistry config) {
config.enableSimpleBroker("/topic/", "/queue/");
config.setApplicationDestinationPrefixes("/app");
}
@Override
public void registerStompEndpoints(StompEndpointRegistry registry) {
registry.addEndpoint("/greeting");
}
}使用 @EnableWebSocketMessageBroker,我們啓用了基於WebSocket的、通過STOMP協議進行的消息代理,STOMP代表流式文本定向消息協議。需要注意的是,此註解必須與@Configuration註解一起使用。
雖然並非強制要求擴展AbstractWebSocketMessageBrokerConfigurer,但為了快速示例,自定義導入的配置會更方便。
在第一個方法中,我們設置了一個基於內存的消息代理,用於將消息帶回客户端,目的地以“/topic”和“/queue”為前綴。
並且,在第二個方法中,我們註冊了stomp端點,位於“/greeting”。
如果需要啓用SockJS,則需要修改註冊部分:
registry.addEndpoint("/greeting").withSockJS();3. 通過攔截器獲取會話 ID
獲取會話 ID 的一種方法是添加一個 Spring 攔截器,該攔截器將在握手過程中觸發,並從請求數據中獲取信息。
該攔截器可以直接在 WebSocketConfig 中添加。
@Override
public void registerStompEndpoints(StompEndpointRegistry registry) {
registry
.addEndpoint("/greeting")
.setHandshakeHandler(new DefaultHandshakeHandler() {
public boolean beforeHandshake(
ServerHttpRequest request,
ServerHttpResponse response,
WebSocketHandler wsHandler,
Map attributes) throws Exception {
if (request instanceof ServletServerHttpRequest) {
ServletServerHttpRequest servletRequest
= (ServletServerHttpRequest) request;
HttpSession session = servletRequest
.getServletRequest().getSession();
attributes.put("sessionId", session.getId());
}
return true;
}}).withSockJS();
}4. WebSocket 端點
從 Spring 5.0.5.RELEASE 版本開始,由於 @SendToUser 標註的改進,無需進行任何自定義,因為該標註允許我們通過 “/user/{sessionId}/…” 而不是 “/user/{user}/…” 將消息發送到用户目標。
這意味着該標註依賴於輸入消息的會話 ID,從而有效地將回復發送到私有會話目標。
@Controller
public class WebSocketController {
@Autowired
private SimpMessageSendingOperations messagingTemplate;
private Gson gson = new Gson();
@MessageMapping("/message")
@SendToUser("/queue/reply")
public String processMessageFromClient(
@Payload String message,
Principal principal) throws Exception {
return gson
.fromJson(message, Map.class)
.get("name").toString();
}
@MessageExceptionHandler
@SendToUser("/queue/errors")
public String handleException(Throwable exception) {
return exception.getMessage();
}
}重要的是要指出,<em/>@SendToUser</em/> 表示消息處理方法的返回值應作為 <em/>Message</em/> 對象發送到指定的destination(s)</strong/>,並且在目標位置前會添加 <em/>/user/{username}</em/> 路徑。
5. WebSocket 客户端
WebSocket 客户端是與 WebSocket 服務器建立持久連接並進行通信的應用程序。它允許客户端和服務器之間進行實時雙向通信,無需客户端主動發起請求。
主要功能:
- 連接建立: 客户端通過指定 WebSocket 服務器的地址和端口,建立持久的 TCP 連接。
- 消息發送與接收: 客户端可以向服務器發送和接收 WebSocket 消息。
- 狀態管理: 客户端需要管理連接的狀態,例如連接打開、連接關閉等。
- 錯誤處理: 客户端需要處理連接中斷、消息錯誤等異常情況。
示例 (JavaScript):
// 建立 WebSocket 連接
const websocket = new WebSocket("ws://localhost:8080");
// 監聽連接事件
websocket.onopen = () => {
console.log("WebSocket 連接已建立");
};
// 監聽消息事件
websocket.onmessage = (event) => {
console.log("接收到的消息:", event.data);
};
// 監聽關閉事件
websocket.onclose = (event) => {
console.log("WebSocket 連接已關閉:", event);
};
function connect() {
var socket = new WebSocket('ws://localhost:8080/greeting');
ws = Stomp.over(socket);
ws.connect({}, function(frame) {
ws.subscribe("/user/queue/errors", function(message) {
alert("Error " + message.body);
});
ws.subscribe("/user/queue/reply", function(message) {
alert("Message " + message.body);
});
}, function(error) {
alert("STOMP error " + error);
});
}
function disconnect() {
if (ws != null) {
ws.close();
}
setConnected(false);
console.log("Disconnected");
}創建了一個新的 WebSocket,指向 “/greeting” ,用於 WebSocketConfiguration 的映射。
當我們將客户端訂閲到 “/user/queue/errors” 和 “/user/queue/reply” 時,我們使用上一節中標記的信息。
如你所見,@SendToUser 指向 “queue/errors”,但消息將被髮送到 “/user/queue/errors”。
6. 結論
在本文中,我們探討了如何直接向用户或會話 ID 發送消息,使用 Spring WebSocket 實現。