1. Introduction
HTTP(超文本傳輸協議)是一種無狀態的請求-響應協議。其簡潔的設計使其具有很強的可擴展性,但由於每個請求/響應都需要傳輸大量的開銷,因此不適合並且效率低下用於高度互動的實時 Web 應用程序。
由於 HTTP 是同步的,而實時應用程序需要異步,因此諸如輪詢或長輪詢(Comet)之類的解決方案往往很複雜且低效。
為了解決上述問題,我們需要一種基於標準的、雙向和全雙工協議,該協議可供服務器和客户端使用,這導致了 JSR 356 API 的引入——在本文中,我們將展示其使用示例。
2. 設置
讓我們將 Spring WebSocket 依賴項包含到我們的項目中:
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-websocket</artifactId>
<version>6.0.13</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-messaging</artifactId>
<version>6.0.13</version>
</dependency>我們可以從 Maven Central 獲取依賴項的最新版本,包括 spring-websocket 和 spring-messaging。
3. STOMP
Stream Text-Oriented Messaging Protocol (STOMP) 是一種簡單、互操作的協議格式,允許客户端和服務器通過幾乎所有消息代理進行通信。它是一種替代方案,用於 AMQP(高級消息隊列協議)和 JMS(Java 消息服務)。
STOMP 定義了客户端和服務器通過消息語義進行通信的協議。這些語義建立在 WebSockets 之上,並定義了映射到 WebSockets 幀的幀。
使用 STOMP 提供了在不同編程語言中開發客户端和服務器的靈活性。在本示例中,我們將使用 STOMP 用於客户端和服務器之間的消息傳遞。
4. WebSocket 服務器
你可以閲讀有關構建 WebSocket 服務器的文章。
5. WebSocket 客户端
要與 WebSocket 服務器進行通信,客户端必須通過向帶有正確設置的 Upgrade 標頭的一台服務器發送 HTTP 請求來初始化 WebSocket 連接:
GET ws://websocket.example.com/ HTTP/1.1
Origin: http://example.com
Connection: Upgrade
Host: websocket.example.com
Upgrade: websocket請注意,WebSocket URL 使用 ws 和 wss 協議,其中 wss 協議表示安全的 WebSockets。
服務器在 WebSockets 支持啓用時,會通過發送 Upgrade 頭部響應。
HTTP/1.1 101 WebSocket Protocol Handshake
Date: Wed, 16 Oct 2013 10:07:34 GMT
Connection: Upgrade
Upgrade: WebSocket一旦該過程(也稱為 WebSocket 握手)完成,相同的 TCP/IP 連接上將用 WebSocket 連接替換初始 HTTP 連接,隨後雙方可以共享數據。
此客户端連接由 WebSocketStompClient 實例發起。
5.1. WebSocketStompClient
如第3部分所述,首先需要建立 WebSocket 連接,這通過 WebSocketClient 類完成。
WebSocketClient 可以通過以下方式進行配置:
- StandardWebSocketClient,由任何 JSR-356 實現(如 Tyrus)提供
- JettyWebSocketClient,由 Jetty 9+ 原生 WebSocket API 提供
- Spring 的 WebSocketClient 的任何實現
在我們的示例中,我們將使用 StandardWebSocketClient,它是 WebSocketClient 的一個實現。
WebSocketClient client = new StandardWebSocketClient();
WebSocketStompClient stompClient = new WebSocketStompClient(client);
stompClient.setMessageConverter(new MappingJackson2MessageConverter());
StompSessionHandler sessionHandler = new MyStompSessionHandler();
stompClient.connect(URL, sessionHandler);
new Scanner(System.in).nextLine(); // Don't close immediately.
默認情況下,WebSocketStompClient 支持 SimpleMessageConverter。由於我們處理的是 JSON 消息,因此將消息轉換器設置為 MappingJackson2MessageConverter,以便將 JSON 負載轉換為對象。
連接到端點時,我們傳遞一個 StompSessionHandler 實例,該實例處理諸如 afterConnected 和 handleFrame 這樣的事件。
如果我們的服務器具有 SockJs 支持,則我們可以修改客户端以使用 SockJsClient 而不是 StandardWebSocketClient。
5.2. <em StompSessionHandler</em>>
我們可以使用 <em StompSession</em>> 訂閲 WebSocket 主題。 這可以通過創建 <em StompSessionHandlerAdapter</em>> 的實例來實現,該類又實現了 <em StompSessionHandler</em>>。
<em StompSessionHandler</em>> 提供 STOMP 會話的生命週期事件。 這些事件包括會話建立時的回調以及在發生故障時的通知。
一旦 WebSocket 客户端連接到端點,<em StompSessionHandler</em>> 會收到通知,並調用 <em afterConnected()</em>> 方法,其中我們使用 <em StompSession</em>> 訂閲主題:
@Override
public void afterConnected(
StompSession session, StompHeaders connectedHeaders) {
session.subscribe("/topic/messages", this);
session.send("/app/chat", getSampleMessage());
}
@Override
public void handleFrame(StompHeaders headers, Object payload) {
Message msg = (Message) payload;
logger.info("Received : " + msg.getText()+ " from : " + msg.getFrom());
}請確保 WebSocket 服務器正在運行,並運行客户端,消息將在控制枱中顯示。
INFO o.b.w.client.MyStompSessionHandler - New session established : 53b993eb-7ad6-4470-dd80-c4cfdab7f2ba
INFO o.b.w.client.MyStompSessionHandler - Subscribed to /topic/messages
INFO o.b.w.client.MyStompSessionHandler - Message sent to websocket server
INFO o.b.w.client.MyStompSessionHandler - Received : Howdy!! from : Nicky
6. 結論
在本快速教程中,我們已實現了一個基於 Spring 的 WebSocket 客户端。