打開一般網站時,瀏覽器的工作方式很直觀:你要求一個頁面,伺服器把頁面送回來;你再要求一份資料,伺服器再回一份資料。這就是典型的 HTTP 請求與回應模型。

但如果今天做的是聊天室,事情就奇怪了。

假設你正在等朋友傳訊息。瀏覽器不知道訊息什麼時候會出現,而傳統 HTTP 又通常由瀏覽器先開口。最直覺的做法,就是每隔一秒問一次伺服器:「有新訊息嗎?」

沒有。

一秒後再問:「現在有嗎?」

還是沒有。

再一秒:「現在呢?」

這種方法真的能做出聊天室,但它也非常像一個每秒打電話去櫃台問包裹到了沒的人。即使答案永遠是「沒有」,雙方仍然得不斷處理新的詢問。

WebSocket 要解決的,就是這類「資料可能隨時從伺服器端出現」的問題。

HTTP 為什麼會需要一直問?

在最常見的 HTTP 使用方式中,瀏覽器是 request 的發起者,伺服器收到後回傳 response。例如:

瀏覽器 → GET /messages
伺服器 → 200 OK + 訊息列表

這非常適合讀文章、載入圖片、查詢 API,因為使用者通常知道自己什麼時候需要資料。

可是即時訊息不同。新資料可能在第 0.2 秒、第 8 秒,甚至十分鐘後才出現。瀏覽器如果沒有持續存在的通道,就必須重新發出請求才能知道伺服器是否有更新。

最簡單的方法叫 polling,也就是輪詢。

例如前端每五秒呼叫一次:

GET /api/messages

這種方法容易理解,也不一定是錯的。如果資料每幾分鐘才更新一次,輪詢甚至可能是最簡單可靠的方案。

問題出現在「即時性」要求愈高時。若改成每秒、每 500 毫秒甚至更頻繁地查詢,大量請求可能只得到「沒有新資料」。HTTP header、連線處理、驗證與伺服器工作都會產生成本,而且使用者仍得等待下一次輪詢才看得到更新。

WebSocket 的核心:連線不要立刻關掉

WebSocket 採取另一種思路:既然雙方之後還要一直講話,那就建立一條連線,先不要關掉。

一開始,瀏覽器仍會透過 HTTP 向伺服器提出升級連線的要求。若伺服器同意,雙方會完成 WebSocket handshake,接著這條連線便不再只是普通的一次性 HTTP request/response。

概念上可以想成:

瀏覽器 ⇄ WebSocket 連線 ⇄ 伺服器

連線建立後,兩邊都可以主動傳資料。

使用者送出訊息時,瀏覽器可以傳給伺服器;另一位使用者送出訊息時,伺服器也能立即把事件推給你的瀏覽器,不需要等你的瀏覽器下一次發問。

這個「雙方都能主動說話」的特性稱為 full-duplex,也就是全雙工通訊。

它和普通 HTTP 最大差在哪?

差異不只是「比較快」。

HTTP request/response 比較像寄問卷:你送出一個問題,收到一次答案。下一個問題通常又是一個新的請求。

WebSocket 比較像打電話:連線接通後,雙方可以在同一通通話裡持續交談,不必每講一句話就重新撥號。

因此 WebSocket 特別適合事件頻繁,而且事件發生時間無法預先知道的服務。

例如聊天室中,新訊息一出現就能推送;多人協作編輯器中,另一個人的游標或修改可以快速同步;即時儀表板可以持續收到新數據;線上遊戲也可能透過長連線交換部分即時狀態。

WebSocket URL 為什麼是 ws:// 和 wss://?

你可能看過這樣的網址:

ws://example.com/socket

或:

wss://example.com/socket

ws 代表 WebSocket,而 wss 可以理解成受到 TLS 保護的 WebSocket。概念上類似 HTTP 與 HTTPS 的差異。

正式網站通常會使用 wss://,尤其當網頁本身透過 HTTPS 提供時。這能讓傳輸內容在網路途中受到加密保護,避免直接以明文暴露。

但加密不代表應用程式就自動安全。伺服器仍然必須處理身分驗證、授權、輸入驗證、速率限制與其他安全問題。

建立連線後,伺服器怎麼知道要把資料傳給誰?

這是 WebSocket 真正進入後端工程後的重要問題。

假設有三個使用者 A、B、C,各自建立一條 WebSocket 連線。伺服器通常需要記住哪些連線屬於哪些使用者,或哪些連線加入了哪個房間。

當 A 在聊天室 room-42 發出訊息時,後端可以找到同一房間中的 B、C 連線,再把訊息送出去。

所以 WebSocket 並不是「打開就會自動變聊天室」。它只提供持續的雙向通訊能力。房間、使用者、訊息格式、權限、重連與資料保存,仍然是應用程式自己的責任。

如果連線斷掉呢?

長連線一定會遇到斷線。

手機可能從 Wi-Fi 切換到行動網路,筆電可能睡眠,基地台可能短暫失去訊號,伺服器也可能重新部署。WebSocket 不會讓網路突然變得永遠可靠。

因此真正的即時系統通常要設計 reconnect,也就是重新連線機制。

但只重新連上還不夠。

假設你在 10:00:03 斷線,10:00:08 才重新連上,而這五秒之間聊天室出現三則訊息。若系統只從「重新連線後」開始接收 WebSocket 事件,那三則訊息就可能消失。

常見設計因此會把 WebSocket 與一般資料 API 搭配使用:HTTP 負責取得可靠的目前狀態或歷史紀錄,WebSocket 負責把最新事件即時送過來。重新連線後,再透過 API 補齊斷線期間遺漏的資料。

心跳:你還活著嗎?

另一個問題是,連線看起來存在,不代表對方真的還在線。

某些網路設備可能悄悄丟掉連線,客戶端也可能突然消失。為了判斷連線是否仍然有效,WebSocket 系統常使用 ping/pong 或應用層 heartbeat。

概念很簡單:一方定期傳一個很小的訊號,另一方回覆。如果長時間沒有回應,就把該連線視為失效並清理。

這不是在傳真正的聊天內容,而是在確認「這條通道還能不能用」。

WebSocket 會取代 REST API 嗎?

通常不會。

這兩者解決的問題不同。

如果你只是要取得一篇文章、更新個人資料、送出表單或查詢一次商品資訊,HTTP API 通常更簡單。它容易快取、容易除錯,也與現有 Web 基礎設施高度相容。

WebSocket 的價值在於持續而即時的雙向事件交換。

實際產品很常同時使用兩者。例如聊天室開啟時:

第一步,用 HTTP GET /messages 取得最近 50 則歷史訊息。

第二步,建立 WebSocket,開始接收新訊息。

第三步,如果 WebSocket 斷線後重連,再用 HTTP 查詢缺少的歷史資料。

這比「所有東西都硬塞進 WebSocket」更容易維護。

那 Server-Sent Events 呢?

如果需求只有「伺服器持續推資料給瀏覽器」,還有另一個常見選項叫 Server-Sent Events,簡稱 SSE。

SSE 的方向主要是伺服器 → 瀏覽器。瀏覽器若要送資料回去,通常仍透過一般 HTTP request。

因此像 AI 逐字輸出回答、新聞更新、伺服器進度通知等情境,不一定需要完整的雙向 WebSocket。若主要需求只是 server push,SSE 往往更單純。

選擇技術時,不應只問「哪個比較新」,而要問資料究竟怎麼流動。

需要頻繁雙向互動,可以考慮 WebSocket;主要由伺服器單向推送,可以評估 SSE;更新頻率很低,普通 polling 甚至就足夠。

WebSocket 的代價

長連線也讓伺服器必須管理更多狀態。

傳統 HTTP request 完成後,這次互動基本就結束了。但 WebSocket 可能維持幾分鐘、幾小時甚至更久。伺服器需要知道哪些 connection 還活著、屬於誰、訂閱什麼頻道。

當服務擴充到多台伺服器時,問題會更明顯。

例如使用者 A 連到 Server 1,B 連到 Server 2。A 發出訊息後,Server 1 必須有方法通知 Server 2,才能把資料推給 B。大型系統因此可能加入 Redis Pub/Sub、message broker 或其他事件基礎設施協調不同節點。

所以「WebSocket 很即時」並不代表「WebSocket 很簡單」。

前端看到的 WebSocket 長什麼樣?

瀏覽器本身提供 WebSocket API。最小概念大致如下:

const socket = new WebSocket('wss://example.com/socket');

socket.addEventListener('open', () => {

console.log('connected');

});

socket.addEventListener('message', (event) => {

console.log('received:', event.data);

});

socket.send('hello');

這幾行程式碼看起來很短,但真正產品還需要處理錯誤、斷線重連、訊息格式、認證與狀態同步。

理解 API 很容易,設計可靠的即時系統才是主要工程工作。

最後,把它想成一條沒有掛掉的電話

如果只記一件事,可以記住這個差異:

普通 HTTP 常見的互動方式,是「我問一次,你答一次」。

WebSocket 則是在一開始建立連線後,把通道保持住,讓瀏覽器與伺服器之後都能主動傳資料。

這就是為什麼聊天室不必每秒問伺服器「有沒有新訊息」。當新訊息真的出現時,伺服器可以直接透過已經存在的連線告訴瀏覽器。

而這也說明了 Web 開發裡一個很重要的觀念:即時功能不是單純把輪詢速度調快。真正的差異,是改變了資料抵達你的方式。

把概念放回真正的系統流程裡看,通常比只背名詞更容易理解它。