WebSocket 入門:簡易聊天室
大家好,我是前端西瓜哥,今天我們用 WebSocket 來實(shí)現(xiàn)一個(gè)簡單的聊天室。
WebSocket 是一個(gè)應(yīng)用層協(xié)議,有點(diǎn)類似 HTTP。但和 HTTP 不一樣的是,它支持真正的全雙工,即不僅客戶端可以主動發(fā)消息給服務(wù)端,服務(wù)端也可以主動發(fā)消息給客戶端。
尤其是后者,讓我們不用再基于 HTTP 長輪詢或短輪詢的低效方式來實(shí)現(xiàn)服務(wù)端通知。相比 HTTP,WebSocket 的服務(wù)端推送更輕量,并能減少服務(wù)端的壓力。
服務(wù)端
nodejs 并沒有提供原生的 WebSocket 模塊。如果要實(shí)現(xiàn),需要基于 net 模塊,根據(jù) WebSocket 標(biāo)準(zhǔn)去做實(shí)現(xiàn)。
因?yàn)閷?shí)現(xiàn)很復(fù)雜,所以西瓜哥我選擇直接用第三庫 ws。
類似 nodejs 原生的 http 等模塊,ws 庫支持 WebSocket 的服務(wù)端或客戶端, 提供偏底層的 API。
我們先實(shí)現(xiàn)服務(wù)端代碼:
每當(dāng)一個(gè)客戶端進(jìn)行了 websocket 連接,都會觸發(fā) wsServer 的 connection 事件,然后拿到一個(gè) ws 對象。
這個(gè) ws 對象代表了某個(gè)客戶端和服務(wù)端的連接,我們可以通過它來接收對應(yīng)客戶端的消息,并讓服務(wù)端對指定客戶端進(jìn)行主動消息推送。
新創(chuàng)建的 ws 對象會在建立連接時(shí)保存到 wsServer.clients 集合下,并在關(guān)閉連接后移除。所以我們可以利用這個(gè) wsServer.clients 來進(jìn)行廣播,實(shí)現(xiàn)聊天室功能。
客戶端
客戶端使用原生的 WebSocket 對象,來和服務(wù)端進(jìn)行 WebSocket 連接。
效果
簡易聊天室
改為使用 Socket.IO
ws 庫是偏底層的實(shí)現(xiàn),比較簡單。
另一個(gè)庫 Socket.IO 的底層使用了 ws,并做功能上的增強(qiáng),提供更多的能力。
相比 ws,Socket.IO 能夠做到:
- 如果瀏覽器不支持 WebSocket,回退為 HTTP 長輪詢方案來模擬 WebSocket( WebSocket 于 2011 年完成 RFC,已經(jīng)很久了,目前來說主流瀏覽器都已經(jīng)支持 WebSocket 了,還不支持 WebSocket 的瀏覽器是屑)。
- 使用心跳包機(jī)制實(shí)現(xiàn)了自動重連。
- 包緩存。斷連時(shí)發(fā)送數(shù)據(jù),會將數(shù)據(jù)保存下來,等重新連接后再發(fā)送。
- 自定義事件支持。
- 廣播。
相比自己去一個(gè)個(gè)實(shí)現(xiàn),使用流行的輪子可能是更好的選擇。
我們將前面的功能用 Socket.IO 實(shí)現(xiàn)一下。
服務(wù)端:
需要特別注意的是,Socket.IO 的 v3.x 版本開始,默認(rèn)不允許跨域,需要在配置顯式設(shè)置為允許跨域。
客戶端:
Socket.IO 優(yōu)點(diǎn)是實(shí)現(xiàn)了生產(chǎn)環(huán)境需要的底層非業(yè)務(wù)能力,讓我們能更心無旁騖地去編寫業(yè)務(wù)代碼。
缺點(diǎn)是丟失了靈活性。因?yàn)樽隽硕ㄖ苹?,所以需要配套使?Socket.IO 的客戶端和服務(wù)端庫的包,某種意義脫離了網(wǎng)絡(luò)協(xié)議標(biāo)準(zhǔn)。在出現(xiàn)跨語言(比如前端是 JS,后端是 Java)的場景時(shí),需要提供對應(yīng)的語言的 Socket.IO 實(shí)現(xiàn)。
demo
demo 已經(jīng)放到 github 上了,使用方法在 README.md 中有說明。
https://github.com/F-star/websocket-chat-demo
結(jié)尾
本文演示了 WebSocket 簡易的聊天室功能是如何實(shí)現(xiàn)的,希望對你有所幫助。