項目背景介紹
最近在寫的項目中存在著社交模塊,需要實現這樣的一個功能:當發生了用戶被點贊、評論、關注等操作時,需要由服務器向用戶實時地推送一條消息。最終完成的項目地址為:https://github.com/noiron/socket-message-push,這裡將介紹一下實現的思路及部分代碼。
項目的流程中存在著這樣的幾個對象:
事件處理的流程如下:
考慮消息推送服務器上必須記錄下當前在線用戶的信息,這樣才能向特定的用戶推送消息。所以當用戶登錄時,必須將自身的用戶信息發到Node.js 服務器上。為了達到這種雙向的實時消息傳遞,很明顯地考慮用WebSocket 來實現。既然我們在消息推送服務器上使用了Node.js,我們就有了一個很方便的選項:socket.io。
Socket.io 介紹
Socket.io是一個用JavaScript 實現的實時雙向通信的庫,利用它來實現我們的功能會很簡單。
socket.io 包含兩個部分:
可以看看如下的socket.io 的示例代碼,它給出了socket.io 發出及監聽事件的基本用法:
io.on('connection', function(socket){ socket.emit('request', /* */); // emit an event to the socket io.emit('broadcast', /* */); // emit an event to all connected sockets socket.on('reply', function(){ /* */ }); // listen to the event});關於Socket.io 還有一點需要注意:Socke.io 並不完全是WebSocket 的實現。
Note: Socket.IO is not a WebSocket implementation. Although Socket.IO indeed uses WebSocket as a transport when possible, it adds some metadata to each packet: the packet type, the namespace and the ack id when a message acknowledgement is needed.
接下來我們需要用Express.js 來建立一個服務器端程序,並在其中引入Socket.io。
Node.js 服務器的搭建
利用Express.js 搭建基礎服務器
我們使用了Express.js 來搭建Node.js 消息推送服務器,先利用一個簡要的例子來瀏覽其功能:
// server.jsconst express = require('express');const app = express();const path = require('path');const http = require('http').Server(app);const port = 4001;app.use(express.static(path.join(__dirname, 'public')));app.get('/', function(req, res) { res.sendFile(__dirname + '/public/index.html');});app.get('/api', function(req, res) { res.send('.');});http.listen(port, function() { console.log(`listening on port:${port}`);});將上面的代碼保存為server.js,新建一個public 文件夾,在其中放入index.html 文件。運行以下命令:
node server.js
現在即可在localhost:4001 查看效果了。
引入Socket.io
現在已經有了一個基礎的Express 服務器,接下來需要將Socket.io 加入其中。
const io = require('socket.io')(http);io.on('connection', function(socket) { console.log('a user connected'); socket.broadcast.emit('new_user', {});}這裡的io 監聽connection 事件,當client 與server 建立了連接之後,這裡的回調函數會被調用( client中的代碼將在下一節介紹)。
函數的參數socket 代表的是當前的client 和server 間建立的這個連接。可在client 程序中將這個建立的socket 連接打印出來,如下圖所示:
其中的id 屬性可以用於標識出這一連接,從而server 可以向特定的用戶發送消息。
socket.broadcast.emit('new_user', {});這一行代碼表示socket 將向當前所有與server 建立了連接的client(不包括自己) 廣播一條名為new_user 的消息。
後端推送消息的處理流程
對用戶信息的處理
方便起見,這裡只用一個數組保存用戶信息,實際工作中可以根據需要放入數據庫中保存。
global.users = []; // 記錄下登錄用戶的tokenId, socketId
當用戶登錄時, client 會向server 發送user_login 事件,服務器接收到後會做如下操作:
socket.on('user_login', function(info) { const { tokenId, userId, socketId } = info; addSocketId(users, { tokenId, socketId, userId });});addSocketId() 會向users 數組中添加用戶信息,不同用戶通過tokenId 進行區分,每個用戶有一個socketIds 數組,保存可能存在的多個socketId。該函數的具體代碼可見src/utils.js 文件。
同理,還有一個deleteSocketId() 函數用於刪除用戶信息,代碼可見同一文件。
在獲取了用戶的tokenId 之後,就需要找到對應的socketId,然後向特定用戶推送消息。
// 只向id = socketId 的這一連接發送消息io.sockets.to(socketId).emit('receive_message', { entityType, data});服務器的思路大致如此,接下來介紹客戶端中是如何進行相應的處理的。
客戶端
Socket.io 的初始化
首先在html 文件中引入Socket.io 的client 端文件,例如通過CDN 引入:
<script src="https://cdnjs.cloudflare.com/ajax/libs/socket.io/2.0.3/socket.io.js"></script>
其它的引入方式:
<script src="/socket.io/socket.io.js"></script>const io = require('socket.io-client');// or with import syntaximport io from 'socket.io-client';引入Socket.io 後就獲得了io 函數,通過它來與消息推送服務器建立連接。
// 假設你將Node 服務器部署後的地址為:https://www.example.com/ws// 則: WS_HOST = 'https://www.example.com'const msgSocket = io(`${WS_HOST}`, { secure: true, path: '/ws/socket.io'});如果監聽本地:
const msgSocket = io('http://localhost:4001');這裡如果寫成io('https://www.example.com/ws') 會出現錯誤,需要將/ws 寫入path中。
為了能在其它文件使用這一變量,可將msgSocket 作為一個全局變量:
window.msgSocket = msgSocket;
用戶建立連接
// 用戶登錄時,向服務器發送用戶的信息。服務器會在收到信息後建立socket 與用戶的映射。 msgSocket.emit('user_login', { userId, socketId: msgSocket.id, tokenId});接收到推送的消息後的處理
// WebSocket 連接建立後,監聽名為receive_message 的事件msgSocket.on('receive_message', msg => { store.dispatch({ type: 'NEW_SOCKET_MSG', payload: msg });});當WebSocket 服務器向客戶端推送了消息之後,客戶端需要監聽receive_message 事件,接收到的參數中有相應待處理的信息。
由於使用了Redux 進行數據的處理,所以這裡dispatch 了一個NEW_SOCKET_MSG action,後續則是常規的redux 處理流程了。
項目的使用
GitHub 上的項目地址:https://github.com/noiron/socket-message-push
npm run dev
即可在devlopment 環境下進行測試,現在你就有了一個運行在4001端口的消息推送服務器了。
但是這裡並沒有後端的服務器來向我們發送消息,所以我們將利用Postman 來模擬發送消息。
為了展示程序的功能,在項目的client 文件夾下放置了一個index.html 文件。注意這個文件並不能用在實際的項目中,只是用來顯示消息推送的效果而已。
在開啟了服務器之後,打開client/index.html,根據提示隨意輸入一個tokenId 即可。
現在利用Postman 向localhost:4001/api post 如下的一條信息:
{ // tokens 數組表示你想向哪個用戶推送消息"tokens": ["1", "2"], "data": "You shall not pass!!!"}至此,如果一切順利,你應該能夠在client 的控制台中看到收到的消息了。
你可以打開多個client 頁面,輸入不同的tokenId,然後檢查消息是否發送給了正確的用戶。
總結
以上所述是小編給大家介紹的利用Socket.io 實現消息實時推送功能,希望對大家有所幫助,如果大家有任何疑問請給我留言,小編會及時回復大家的。在此也非常感謝大家對武林網網站的支持!