Socket.IO打造基礎聊天室
![1632709-5179c504964a37a6.png](https://i.iter01.com/images/3003a51e9165d05e9443ce9c9bc8640a68e09190bd868d46b2df68006b7c8cf6.png)
01 Socket.io 簡介
- 一個100%由 JavaScript 實現、基於Node.js的用於實時通訊、跨平臺的開源框架,它包括了客戶端的 JavaScript 庫和 伺服器端的 Node.js 服務。
- 實現了對於其他語言的支援,如 Java、C++、Swift。
- 提供了一個與 WebSocket 類似的通用 API:
![1632709-4e0b26377ba8a22d.png](https://i.iter01.com/images/8a2ff163089cacad83644695a26203b4600514aa50b208f95903784c2106697e.png)
【主要特點】:
(1) 可靠性(Reliability):依賴 Engine.IO, 首先建立長輪詢,然後試著升級到更好的傳輸方式,如 WebSocket。
(2)自動重連(Auto-reconnection support):除非手動設定,否則當客戶端斷開連線時會一直嘗試重連。
(3)心跳檢測(Disconnection detection):在 Engine.IO 層面實現的心跳檢測機制,允許伺服器和客戶端知道哪一方不再響應。
Engine.IO:為 Socket.IO 實現的基於傳輸、跨瀏覽器/跨裝置的雙向通訊層。
(4)其它特點(如下圖):
![1632709-c14b2ac9cf75bce3.png](https://i.iter01.com/images/e328e28681f8d5f81c5dbbea27ab7992ec481290c6a826c8fd15b08282baf2af.png)
02 工作流程
Socket.IO 底層是 Engine.IO,這個庫實現了跨平臺的雙向通訊,使用下面的傳輸方式封裝了一套自己的 Socket 協議(EIO Socket)。
- polling: XHR / JSONP polling transport
- websocket: WebSocket transport
預設情況下,一個完整的 EIO Socket 包括多個 XHR 和 WebSocket 連線:
![1632709-6d70c914b54cfddf.png](https://i.iter01.com/images/eec0f9f9047e4a4aab3f6d4b9c4faebf281caf8869ef6dad53d3754593b3d213.png)
![1632709-648f88757479781d.png](https://i.iter01.com/images/5d97ca29482b7776b3cb095e4eadb4b092af9f0cf1bb26a25abb79c90f509529.png)
EIO Socket 會首先發起XHR長輪詢,然後服務端會返回以下欄位:
- 0:open標誌
-
sid
:當前連線的socket id -
upgrade
:表示可以把連線方式從長輪詢升級到 websocket -
pingInterval
:心跳間隔 -
pingTimeout
:心跳超時時間
前端收到握手的 upgrades 後,EIO 會檢測瀏覽器是否支援 WebSocket,如果支援,就會啟動一個 WebSocket 連線,然後通過這個 WebSocket 往伺服器發一條內容為 probe, 型別為 ping 的資料。如果這時伺服器返回了內容為 probe, 型別為 pong 的資料,前端就會把前面建立的 HTTP 長輪詢停掉,後面只使用 WebSocket 通道進行收發資料。(socket.io 的詳細工作流程是怎樣的?)
心跳檢測:
EIO Socket生命週期內,會間隔一段時間 ping - pong 一次,用來測試網路是否正常
![1632709-ad539266458e25ef.png](https://i.iter01.com/images/9d5e9d989db00b0d5957c4d6d9ee9327102cb0c36a919b40a4d858dc1b58567c.png)
綠色:傳送;白色:接收。
型別—> 2:ping,3:pong,4:message。
Socket.IO 在 Engine.IO 的基礎上做了一些封裝,比如 Socket.IO 裡面這樣的程式碼:
io.emit('add user', 'm') ;
在 Engine.io 裡面是這樣:
eio.send('message', '2["add user","m"]') ; // 2 是 socket.io 定義的包型別
因此,message的型別4後面有個2。
【傳輸機制設定】:
Socket.io 為我們提供了選項,它的預設情況是以長輪詢開始,我們也可以手動設定成只使用 websocket 方式來進行通訊。
// 設定成只使用 websocket
const socket = io({
transports: ['websocket']
});
// 重連時,重設選項
// 防止 websocket 可能因為代理、防火牆、瀏覽器等原因連線失敗socket.on('reconnect_attempt', () => {
socket.io.opts.transports = ['polling', 'websocket'];
});
![1632709-6609cb1bb10298c7.png](https://i.iter01.com/images/0f2b3d85bdc05f9fe6dcec1a2841f307379f6d72a4204e4bcf0c2fbf6e60c6f7.png)
03 核心方法
![1632709-191c736e154d262a.png](https://i.iter01.com/images/710b43b36edb7fed5d7bb2ae30b53ce19be7e041b6d13e8f9af20995575e0b30.png)
Socket.io 的核心函式:emit
和 on
socket.emit(eventName[, ...args][, ack]):用來發射(觸發)一個事件
-
eventName
(string):事件名 -
args
:要傳送的資料 -
ack
(Function):回撥函式,一般省略,如需對方接受到資訊後立即得到確認時需要用到 - Returns
Socket
socket.emit('ferret', 'tobi', (data) => {
console.log(data); // data will be 'woot'
});
// server:
// io.on('connection', (socket) => {
// socket.on('ferret', (name, fn) => {
// fn('woot');
// });
// });
socket.on(eventName, callback):用來監聽一個 emit 發射的事件
-
eventName
(string):監聽的事件名 -
callback
(Function):匿名函式,接收對方發來的資料,該匿名函式的第一個引數為接收的資料,若有第二個引數,則為要返回的函式 - Returns
Socket
socket.on('news', (data) => {
console.log(data);
});
// with multiple arguments
socket.on('news', (arg1, arg2, arg3, arg4) => {
// ...
});
// with callback
socket.on('news', (cb) => {
cb(0);
});
Socket.io 提供了三種預設的事件(客戶端和伺服器都有):
-
connect
:當與對方建立連線後自動觸發; -
message
:當收到對方發來的資料後觸發; -
disconnect
:當對方關閉連結後觸發。
除了 socket.io 自身提供的事件之外,還支援自定義事件,豐富了通訊:
// 如:
socket.on(‘new message’, function(data) {});
socket.emit(‘new message’, { message: message });
【服務端廣播的三種情況】:
![1632709-a57c9983231965c3.png](https://i.iter01.com/images/bc3b0186813e302aa055bfa3d8250a8a30daef7d0cddc1774d39c8fe6b2e189e.png)
客戶端:
const socket = io();
// 監聽事件
socket.on(‘message’, (data) => {});
// 觸發事件
socket.emit(‘message’, { message });
服務端:
io.on(‘connection’, function(socket) {
socket.on(‘message’, function(data) {
// 1.廣播給自己
socket.emit(‘message’, data);
// 2. 廣播給除了自己的其它客戶端
socket.broadcast.emit(‘message’, data);
// 3. 廣播給所有客戶端
io.emit(‘message’, data); // 等同於 io.sockets.emit()
});
});
04 Rooms 和名稱空間
【作用】:減少TCP連線數的同時區分不同的通訊頻道(在不同的路由層面能體現該作用,具體請參考 socket.io 中namespace 和 room的概念)、實現私聊
預設的名稱空間:io.sockets、io
io.on('connection', function(socket){
socket.on('disconnect', function(){ });
});
自定義名稱空間:
// 伺服器端
var nsp = io.of('/my-namespace');
nsp.on('connection', function(socket){
socket.on('disconnect', function(){ });
});
// 客戶端
var socket = io('/my-namespace');
Rooms:
// 自定義room
io.on('connection', function(socket){
socket.join('some room')); // 加入房間
socket.leave('some room'); // 離開房間
});
// 向房間裡的所有客戶端傳送訊息
io.to('some room').emit('some event');
// 預設房間(每一個id一個room)
socket.on('say to someone', function(id, msg){
socket.broadcast.to(id).emit('my message', msg);
});
獲取房間資訊:socket.adapter.rooms
![1632709-58d5770a1e4f43d2.png](https://i.iter01.com/images/935939a5ed447ce3146bec553cb48eff9e4aec0ba63ba0bf0cbc904176c1de5f.png)
預設情況下,每一個 id 便自成一個房間,房間名為
socket.id
(指定名稱空間之後,前面會帶上名稱空間);
自定義房間之後,原先的預設房間仍然存在;
房間為一個物件,包含當前進入房間的 sockets 以及長度。
05 打造基礎聊天室
從官網最基礎的聊天小例子入門,又分析了一下 Demos 中的 Chat demo 原始碼之後,自己試著用 react 實現了一遍,具體的功能及原理如下:
-
最基礎聊天功能
【實現原理】:服務端運用
io.emit
進行廣播給每個建立連線的客戶端。 -
登入(Chat Demo)
【核心操作】:使用者登入時(
login
事件),服務端為當前的客戶端儲存 username。socket.username = username
-
顯示使用者進入/離開
【實現原理】:使用者登入(
login
)時,觸發user joind
事件;使用者斷開連線(disconnection
)時,觸發user left
事件—> 均通過socket.broadcase.emit
廣播給其它客戶端。 -
顯示當前聊天室人數
【實現原理】:操作 numUsers 變數—> 監聽
connection
事件:++numUsers;監聽disconnection
事件:--numUsers 。 -
顯示各自暱稱
【實現原理】:觸發
chat
事件的時候將當前連線的 username 通過io.emit
廣播給每個給客戶端。 -
提示對方正在輸入
【實現原理】:監聽
typing
和stop typing
事件 —> 均通過socket.broadcast.emit
廣播給其它客戶端。(typing
通過監聽輸入框的onKeyPress
事件進行觸發)注意:當傳送完資訊之後,需要清空“***正在輸入”,客戶端在監聽到
chat
時可將提示置空。 實現私人聊天
![1632709-d45c7c39ae3db043.png](https://i.iter01.com/images/0c9f114b844c5af26f720d2ba2a23c0c642ad27216c4e30a107a2c319d9d0e89.png)
【實現原理】:運用 Room,通過 socket.adapter.rooms
獲取當前 room 的資訊,包括每個 room 中的 id。
// 加入房間
socket.join('some room');
// 離開房間
socket.leave('some room');
// 向房間裡的所有客戶端傳送訊息
io.to('some room').emit('some event');
// 向房間中的除了自己的客戶端傳送訊息
socket.broadcast.to ('some room')
.emit('my message', msg);
自娛自樂的實現版本:
github地址:ioChat
![1632709-7b22fc89edecb9c0.png](https://i.iter01.com/images/31f1af0b93e6c03b7050faa3865458a5b6486dd167465048202fa01b73c9d164.png)
![1632709-820f5ac78dae01e9.png](https://i.iter01.com/images/9105633d349e466f187ec415fb41768fde15a13f60bb9f990ba256e29ace75ed.png)
![1632709-8c924288c50b7f68.png](https://i.iter01.com/images/ee177f94734b7ef74401b3b53661de47e42d7a27f10771ade3d3ce5099517157.png)
ps:下一次我會出一篇聊天室實現 emoji 表情傳送的文章,敬請期待喲~~٩(๑>◡<๑)۶ ~~
§ 參考資料
相關文章
- 基於socket.io打造hybrid除錯頁面除錯
- FastAPI(56)- 使用 Websocket 打造一個迷你聊天室ASTAPIWeb
- 基於Socket.IO實現Android聊天功能Android
- 基於netty的聊天室Netty
- 基於golang的聊天室Golang
- socket.io websocketWeb
- 基於webapi的websocket聊天室(四)WebAPI
- 打造DiTing聊天室之從零開始:準備工作指南
- socket.IO通訊
- Socket.IO 入門
- 聊天室應用開發實踐(二):實現基於 Web 的聊天室Web
- 立足區塊鏈,JASMINER丨茉莉打造元宇宙基礎設施區塊鏈ASM元宇宙
- 基礎軟體打造企業數字化轉型加速引擎
- 國家質量基礎設施NQI助力打造強省市(區)
- 基於webapi的websocket聊天室(番外二)WebAPI
- 基於webapi的websocket聊天室(番外一)WebAPI
- C++ 實現基於TCP的聊天室C++TCP
- 基於 golang + vue + websocket 開發的聊天室GolangVueWeb
- socket.io 原理詳解
- Socket.io 深入理解
- socket.io通訊原理
- 從 0 開始打造聊天室,搞定 Laravel 實時通訊 —— 準備工作Laravel
- 基於 socket.io 快速實現一個實時通訊應用
- 基於 flask 結合 Redis 的簡單聊天室FlaskRedis
- Python基於Socket實現簡易多人聊天室Python
- 國家質量基礎設施,為打造質量強國做部署
- 2021WAIC | 大資料基礎軟體助力打造城市數字底座AI大資料
- 城市基礎設施數字化管理:打造安全、智慧的城市生命線
- 構建模組化 CLI:Lerna + Commander 打造靈活的基礎腳手架
- 從 0 開始打造聊天室,搞定 Laravel 實時通訊 —— 配置與繫結Laravel
- 從 0 開始打造聊天室,搞定 Laravel 實時通訊 —— 傳送訊息Laravel
- 【FPGA基礎】Latch基礎FPGA
- 關於socket.io的使用
- Socket.io開發注意點
- socket.io學習記錄
- Socket.IO IM通訊元件元件
- NIO實現聊天室之:一切都要從網路程式設計的基礎開始聊起!程式設計
- Pandas 基礎 (2) - Dataframe 基礎
- 前端基礎之jQuery基礎前端jQuery