打開一般網站時,瀏覽器的工作方式很直觀:你要求一個頁面,伺服器把頁面送回來;你再要求一份資料,伺服器再回一份資料。這就是典型的 HTTP 請求與回應模型。
但如果今天做的是聊天室,事情就奇怪了。
假設你正在等朋友傳訊息。瀏覽器不知道訊息什麼時候會出現,而傳統 HTTP 又通常由瀏覽器先開口。最直覺的做法,就是每隔一秒問一次伺服器:「有新訊息嗎?」
沒有。
一秒後再問:「現在有嗎?」
還是沒有。
再一秒:「現在呢?」
這種方法真的能做出聊天室,但它也非常像一個每秒打電話去櫃台問包裹到了沒的人。即使答案永遠是「沒有」,雙方仍然得不斷處理新的詢問。
WebSocket 要解決的,就是這類「資料可能隨時從伺服器端出現」的問題。
HTTP 為什麼會需要一直問?
在最常見的 HTTP 使用方式中,瀏覽器是 request 的發起者,伺服器收到後回傳 response。例如:
這非常適合讀文章、載入圖片、查詢 API,因為使用者通常知道自己什麼時候需要資料。
可是即時訊息不同。新資料可能在第 0.2 秒、第 8 秒,甚至十分鐘後才出現。瀏覽器如果沒有持續存在的通道,就必須重新發出請求才能知道伺服器是否有更新。
最簡單的方法叫 polling,也就是輪詢。
例如前端每五秒呼叫一次:
GET /api/messages
這種方法容易理解,也不一定是錯的。如果資料每幾分鐘才更新一次,輪詢甚至可能是最簡單可靠的方案。
問題出現在「即時性」要求愈高時。若改成每秒、每 500 毫秒甚至更頻繁地查詢,大量請求可能只得到「沒有新資料」。HTTP header、連線處理、驗證與伺服器工作都會產生成本,而且使用者仍得等待下一次輪詢才看得到更新。
WebSocket 的核心:連線不要立刻關掉
WebSocket 採取另一種思路:既然雙方之後還要一直講話,那就建立一條連線,先不要關掉。
一開始,瀏覽器仍會透過 HTTP 向伺服器提出升級連線的要求。若伺服器同意,雙方會完成 WebSocket handshake,接著這條連線便不再只是普通的一次性 HTTP request/response。
概念上可以想成:
連線建立後,兩邊都可以主動傳資料。
使用者送出訊息時,瀏覽器可以傳給伺服器;另一位使用者送出訊息時,伺服器也能立即把事件推給你的瀏覽器,不需要等你的瀏覽器下一次發問。
這個「雙方都能主動說話」的特性稱為 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。
因此像 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 開發裡一個很重要的觀念:即時功能不是單純把輪詢速度調快。真正的差異,是改變了資料抵達你的方式。
把概念放回真正的系統流程裡看,通常比只背名詞更容易理解它。