客戶端和伺服器之間的實時通訊對於建立動態和互動式 Web 應用程式至關重要。用於實現此目的的兩種流行技術是伺服器傳送事件 (SSE) 和 Web 套接字。兩者都允許雙向通訊,但它們有不同的用例和實現。
本文旨在探討 SSE 和 WebSocket 之間的差異,並提供一些使用 Java 的程式碼示例。
伺服器傳送事件(SSE):單向更新
伺服器傳送事件 (SSE) 是一種簡單而高效的技術,用於透過 HTTP 將資料從伺服器推送到客戶端。伺服器傳送的事件建立長期執行的 HTTP 連線,允許伺服器將資料推送到客戶端。它非常適合更新主要來自伺服器的場景,例如股票行情、實時新聞源或只有伺服器廣播訊息的聊天應用程式。
import jakarta.ws.rs.GET; import jakarta.ws.rs.Path; import jakarta.ws.rs.Produces; import jakarta.ws.rs.core.MediaType; import jakarta.ws.rs.sse.Sse; import jakarta.ws.rs.sse.SseEventSink;
@Path("/events") public class SSEServer {
@GET @Produces(MediaType.SERVER_SENT_EVENTS) public void getServerSentEvents(@jakarta.ws.rs.core.Context SseEventSink eventSink, @jakarta.ws.rs.core.Context Sse sse) { // Set up a new thread to generate events new Thread(() -> { try { for (int i = 0; i < 20; i++) { // Send events to the client eventSink.send(sse.newEventBuilder().name("sseevent").data(" " + i).build()); Thread.sleep(2000); // Simulate delay } } catch (InterruptedException e) { } finally { // Close the event sink when done eventSink.close(); } }).start(); } }
|
上面的程式碼演示了一個定期傳送模擬資料更新的 SSE 伺服器。
下面演示了訂閱 SSE 端點並顯示接收到的資料:
<h1>Server-Sent Events (SSE) Client</h1>
<div style="background-color: #64a4bd; width: 200px; padding: 10px">
<h4 id="eventId"></h4>
</div>
|
<script> // 處理傳入事件的函式 function handleEvent(event) { var eventsDiv = document.getElementById("eventId"); eventsDiv.innerHTML = 'Received event: ' + event.data + '<br>'; }
// 建立指向 SSE 端點的新事件源 var eventSource = new EventSource("rest/events");
// 附加事件監聽器以處理接收到的事件 eventSource.addEventListener('sseevent', handleEvent); //eventSource.onmessage = handleEvent;
//處理 SSE 錯誤的事件監聽器 eventSource.onerror = function (event) { eventSource.close(); console.error('SSE error:', event);
}; </script>
|
上面程式碼建立一個EventSource物件,訂閱伺服器的 SSE 端點。
好處:
- 簡單性: 伺服器和客戶端的實現都相對簡單。
- 輕量級: 單向通訊使協議保持高效。
- 廣泛的瀏覽器支援: 大多數現代瀏覽器都提供對 SSE 的內建支援。
缺點:
- 單向通訊: 資料只能從伺服器傳輸到客戶端。客戶端不能直接發回訊息。
- 基於文字的資料: SSE 僅限於文字資料(UTF-8 格式)。它無法傳輸影像或影片等二進位制資料。
WebSockets:雙向通訊
WebSockets 透過客戶端和伺服器之間的單個長期連線提供全雙工通訊通道。與 SSE 不同,WebSocket 允許客戶端和伺服器隨時相互傳送訊息。這種多功能性使它們非常適合聊天或協作編輯等互動式應用程式。
下面程式碼定義了用於雙向通訊的 JAX-RS 註釋的 WebSocket 端點。
import jakarta.websocket.OnClose; import jakarta.websocket.OnError; import jakarta.websocket.OnMessage; import jakarta.websocket.OnOpen; import jakarta.websocket.Session; import jakarta.websocket.server.ServerEndpoint; import java.io.IOException;
@ServerEndpoint("/websocket") public class WebSocketServer {
@OnOpen public void onOpen(Session session) { System.out.println("WebSocket opened: " + session.getId()); }
@OnMessage public void onMessage(String message, Session session) { System.out.println("Message received: " + message); try { session.getBasicRemote().sendText("Echo: " + message); // Echo back the message } catch (IOException e) { } }
@OnClose public void onClose(Session session) { System.out.println("WebSocket closed: " + session.getId()); }
@OnError public void onError(Throwable error) { System.out.println("" + error); } }
|
下面演示了連線到 WebSocket 端點以及傳送/接收訊息:
<h1>WebSocket Client</h1> Messages<br/> <textarea id="messages" rows="6" cols="50" style="background-color: E6E6FF; border-radius: 15px;margin-bottom: 10px;width: 40%;padding: 20px"></textarea><br/>
<form id="messageForm" style="margin-bottom: 10px"> <input type="text" id="messageInput" placeholder="Enter message" name="name"> <input type="button" onclick="join()" value="Join"/> <input type="button" onclick="sendmessage()" value="Send"/><br/> <input type="button" onclick="disconnect()" value="Disconnect"/> </form><br/>
|
<script language="javascript" type="text/javascript"> // Create WebSocket connection const socket = new WebSocket('ws://localhost:8080/my-faces-app/websocket');
var messageInput = document.getElementById('messageInput'); var users = document.getElementById('users'); var messages = document.getElementById('messages');
var username;
socket.onopen = function (evt) { onOpen(evt); }; socket.onclose = function (evt) { onClose(evt); }; socket.onerror = function (evt) { onError(evt); }; socket.onmessage = function (evt) { onMessage(evt); };
var output = document.getElementById('output');
function join() { username = messageInput.value; socket.send(username + " joined"); // Clear input field messageInput.value = ''; }
function sendmessage() { socket.send(username + ": " + messageInput.value); // Clear input field messageInput.value = ''; }
function onOpen() { writeToScreen("CONNECTED"); } function onClose() { writeToScreen("DISCONNECTED"); }
function onMessage(evt) { writeToScreen("Message Received: " + evt.data); if (evt.data.indexOf("joined") !== -1) { users.innerHTML += evt.data.substring(0, evt.data.indexOf(" joined")) + "\n"; } else { messages.innerHTML += evt.data + "\n"; } }
function onError(evt) { wrtieToScreen("Error : " + evt.data); }
function disconnect() { socket.close(); }
function writeToScreen(message) { var pre = document.createElement("p"); pre.style.wordWrap = "break-word"; pre.innerHTML = message;
output.appendChild(pre); } </script>
|
WebSocket 具有以下幾個優點:
- 全雙工通訊: 資料可以在兩個方向上無縫流動,從而促進真正的互動性。
- 低延遲: 通訊通道是持久的,最大限度地減少資料交換的延遲。
- 豐富的通訊: WebSocket不僅支援文字,還支援二進位制資料格式,允許更通用的資料傳輸。
WebSocket 雖然功能強大,但也有一些注意事項:
- 複雜性: 實現 WebSockets 涉及握手過程和處理雙向訊息流,使其比 SSE 稍微複雜一些。
- 握手開銷: 建立 WebSocket 連線需要初始握手,與 SSE 相比增加了少量開銷。
WebSockets 與 SSE:比較分析
通訊方向
- 全雙工:客戶端和伺服器都可以傳送資料
- 單向:伺服器向客戶端推送資料
協議
- WebSocket協議
- HTTP/HTTPS 協議
連線建立
- WebSocket需要握手
- SSE使用標準 HTTP 連線
持續連線
- WebSocket保持持久連線
- SSE使用長期 HTTP 連線
訊息型別
- WebSocket支援二進位制和文字訊息
- SSE 僅支援簡訊
伺服器開銷
- WebSocket低的
- SSE 由於 HTTP 標頭和開銷而較高
可擴充套件性
- WebSocket連線數更少,可擴充套件性更好
- SSE 由於多個連線,可擴充套件性可能較差
用法
- WebSocket實時應用程式、遊戲和聊天應用程式
- SSE推送通知、實時更新、股票行情等。
結論
本文探討了使用 WebSocket 與 SSE。透過了解它們的優點和缺點,我們可以做出明智的決定,將實時通訊構建到我們的 Web 應用程式中。總之,伺服器傳送事件 (SSE) 和 Web 套接字是支援 Web 應用程式中客戶端和伺服器之間實時通訊的強大技術。 SSE實現起來比較簡單,非常適合伺服器需要向客戶端推送更新的場景。另一方面,WebSocket 提供全雙工通訊,適合需要雙向通訊的互動性更強的應用程式。