傳統 HTTP 請求-回應模式無法滿足伺服器主動推送需求,WebSocket 作為基於 TCP 的應用層全雙工協議 (RFC 6455),解決了這一痛點。本文涵蓋協議本質、適用場景、瀏覽器/伺服器支援現況,並提供基於 Tomcat 8 + JSR 356 的完整 Java 實戰代碼。
Origin 標頭驗證握手流程:
Upgrade: websocket, Connection: Upgrade, Sec-WebSocket-Key101 Switching Protocols,攜帶 Sec-WebSocket-Accept (Key 加密驗證)| 場景 | 傳統方案 | WebSocket 方案 |
|---|---|---|
| 伺服器主動推送通知 | 輪詢/長輪詢/Comet | 直接推送,零延遲 |
| 即時聊天/協作編輯 | 頻繁 AJAX 請求 | 持久連線,雙向即時 |
| 股票/行情即時推送 | 定時拉取 | 伺服器主動廣播 |
| 遊戲狀態同步 | 短輪詢 | 低延遲雙向通道 |
瀏覽器支援:IE 10+、Chrome 4+、Firefox 4+、Safari 5+、Opera 12+ — 現代瀏覽器全支援。
伺服器支援:Tomcat 7.0.47+、Jetty 9+、Netty、Undertow、Node.js (ws/socket.io) 等。
<dependency>
<groupId>javax.websocket</groupId>
<artifactId>javax.websocket-api</artifactId>
<version>1.1</version>
<scope>provided</scope> <!-- Tomcat 內建實現 -->
</dependency>
import javax.websocket.*;
import javax.websocket.server.ServerEndpoint;
import java.io.IOException;
@ServerEndpoint("/websocket")
public class TestWebSocket {
@OnOpen
public void onOpen(Session session) {
System.out.println("客戶端連線:" + session.getId());
}
@OnMessage
public void onMessage(String message, Session session)
throws IOException, InterruptedException {
System.out.println("收到訊息:" + message);
// 立即回覆
session.getBasicRemote().sendText("伺服器收到:" + message);
// 模擬伺服器主動推送 3 條訊息,間隔 5 秒
for (int i = 0; i < 3; i++) {
Thread.sleep(5000);
session.getBasicRemote().sendText("主動推送 #" + (i+1) + ":" + System.currentTimeMillis());
}
session.getBasicRemote().sendText("推送結束");
}
@OnClose
public void onClose(Session session, CloseReason reason) {
System.out.println("連線關閉:" + reason.getReasonPhrase());
}
@OnError
public void onError(Session session, Throwable throwable) {
System.err.println("錯誤:" + throwable.getMessage());
}
}
<!DOCTYPE html>
<html>
<head><title>WebSocket 測試</title></head>
<body>
<button onclick="connect()">連線</button>
<button onclick="send()">發送訊息</button>
<button onclick="close()">斷開</button>
<div id="log"></div>
<script>
var ws;
function connect() {
ws = new WebSocket('ws://localhost:8080/TestWebSocket/websocket');
ws.onopen = () => log('連線已建立');
ws.onmessage = (e) => log('收到:' + e.data);
ws.onclose = () => log('連線關閉');
ws.onerror = (e) => log('錯誤:' + e);
}
function send() { ws.send('Hello from ' + new Date()); }
function close() { ws.close(); }
function log(msg) {
document.getElementById('log').innerHTML += '<br>' + msg;
}
</script>
</body>
</html>
TCP (傳輸層) ├─ HTTP/1.1 (應用層) —— 請求/回應,半雙工 ├─ HTTP/2 (應用層) —— 多路複用,仍為請求/回應語義 └─ WebSocket (應用層) —— 全雙工,握手借用 HTTP Upgrade
關鍵點:WebSocket 獨立於 HTTP,僅握手階段借用 HTTP 協議升級機制,建立連線後完全脫離 HTTP 語義,直接在 TCP 上傳輸自定義幀。