JavaScript >> Javascript 文檔 >  >> Node.js

帶有 Socket.io 的 Node.js Websocket 示例

什麼是 Websockets?

在過去的幾年裡,一種新型的通信方式開始出現在 Web 和移動應用程序中,稱為 websockets。該協議期待已久,終於在 2011 年被 IETF 標準化,為廣泛使用鋪平了道路。

這個新協議開闢了一條更快、更有效的與客戶端的通信線路。與 HTTP 一樣,websockets 運行在 TCP 連接之上,但它們要快得多,因為我們不必每次要發送消息時都打開新連接,因為只要服務器保持連接狀態,連接就會保持活動狀態或客戶想要的。

更好的是,由於連接永遠不會中斷,我們終於可以使用全雙工通信,這意味著我們可以將數據推送到客戶端,而不必等待他們從服務器請求數據 .這允許來回傳輸數據,這對於實時聊天應用程序甚至遊戲等應用來說非常理想。

Websockets 是如何工作的?

從本質上講,websocket 只是一個允許全雙工通信的 TCP 連接,這意味著連接的任何一方都可以向另一方發送數據,甚至在同一時間。

為了建立這個連接,協議實際上將握手作為一個普通的 HTTP 請求發起,然後使用升級請求 HTTP 標頭進行“升級”,如下所示:

GET /ws/chat HTTP/1.1
Host: chat.example.com
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Key: q1PZLMeDL4EwLkw4GGhADm==
Sec-WebSocket-Protocol: chat, superchat
Sec-WebSocket-Version: 15
Origin: http://example.com

然後服務器發回一個 HTTP 101“交換協議”響應,確認連接將被升級。建立此連接後,將切換到雙向二進制協議,此時可以發送應用程序數據。

為了保持連接打開,協議所要做的就是發送一些 ping/pong 數據包,告訴對方它們仍然存在。為了關閉連接,發送一個簡單的“關閉連接”數據包。

一些 Websocket 示例

在我們可用的 Node.js 的許多不同 websocket 庫中,我選擇在本文中使用 socket.io,因為它似乎是最流行的,並且在我看來也是最容易使用的。雖然每個庫都有自己獨特的 API,但它們也有許多相似之處,因為它們都建立在相同的協議之上,因此希望您能夠將下面的代碼翻譯成您想要使用的任何庫。

對於 HTTP 服務器,我將使用 Express,它是目前最流行的 Node 服務器。請記住,如果您不需要 Express 的所有功能,也可以只使用普通的 http 模塊。不過,由於大多數應用程序都將使用 Express,我們也將使用 Express。

注意 :在這些示例中,我刪除了大部分樣板代碼,因此其中一些代碼無法直接使用。在大多數情況下,您可以參考第一個示例來獲取樣板代碼。

建立連接

為了在客戶端和服務器之間建立連接,服務器必須做兩件事:

  1. 連接到 HTTP 服務器以處理 websocket 連接
  2. 提供socket.io.js 客戶端庫作為靜態資源

在下面的代碼中,您可以看到第 (1) 項在第 3 行完成。第 (2) 項由 socket.io 為您完成(默認情況下) 庫並在路徑 /socket.io/socket.io.js 上提供 .默認情況下,所有 websocket 連接和資源都在 /socket.io 中提供 路徑。

服務器

var app = require('express')();
var server = require('http').Server(app);
var io = require('socket.io')(server);

app.get('/', function(req, res) {
    res.sendFile(__dirname + '/index.html');
});

server.listen(8080);

客戶端也需要做兩件事:

  1. 從服務器加載庫
  2. 調用.connect() 到服務器地址和 websocket 路徑

客戶

<script src="/socket.io/socket.io.js"></script>
<script>
    var socket = io.connect('/');
</script>

如果您將瀏覽器導航到 http://localhost:8080 並使用瀏覽器的開發工具檢查幕後的 HTTP 請求,您應該能夠看到正在執行的握手,包括 GET 請求和生成的 HTTP 101 切換協議響應。

從服務器發送數據到客戶端

好的,現在開始一些更有趣的部分。在本例中,我們將向您展示從服務器向客戶端發送數據的最常用方式。在這種情況下,我們將向一個頻道發送一條消息,該頻道可以被客戶端訂閱和接收。因此,例如,客戶端應用程序可能正在偵聽“公告”頻道,該頻道將包含有關係統範圍事件的通知,例如用戶加入聊天室時。

在服務器上,這是通過等待建立新連接,然後調用 socket.emit() 來完成的 向所有連接的客戶端發送消息的方法。

服務器

io.on('connection', function(socket) {
    socket.emit('announcements', { message: 'A new user has joined!' });
});

客戶

<script src="/socket.io/socket.io.js"></script>
<script>
    var socket = io.connect('/');
    socket.on('announcements', function(data) {
        console.log('Got announcement:', data.message);
    });
</script>

從客戶端發送數據到服務器

但是當我們想以另一種方式發送數據時,從客戶端到服務器,我們會怎麼做呢?它與上一個示例非常相似,同時使用 socket.emit()socket.on() 方法。

服務器

io.on('connection', function(socket) {
    socket.on('event', function(data) {
        console.log('A client sent us this dumb message:', data.message);
    });
});

客戶

免費電子書:Git Essentials

查看我們的 Git 學習實踐指南,其中包含最佳實踐、行業認可的標準以及隨附的備忘單。停止谷歌搜索 Git 命令並真正學習 它!

<script src="/socket.io/socket.io.js"></script>
<script>
    var socket = io.connect('/');
    socket.emit('event', { message: 'Hey, I have an important message!' });
</script>

計算連接用戶數

這是一個很好的學習示例,因為它展示了 socket.io 的更多功能 (如 disconnect event),很容易實現,適用於很多webapp。我們將使用 connectiondisconnect 統計我們網站上的活躍用戶數量的事件,我們將使用當前計數更新所有用戶。

服務器

var numClients = 0;

io.on('connection', function(socket) {
    numClients++;
    io.emit('stats', { numClients: numClients });

    console.log('Connected clients:', numClients);

    socket.on('disconnect', function() {
        numClients--;
        io.emit('stats', { numClients: numClients });

        console.log('Connected clients:', numClients);
    });
});

客戶

<script src="/socket.io/socket.io.js"></script>
<script>
    var socket = io.connect('/');
    socket.on('stats', function(data) {
        console.log('Connected clients:', data.numClients);
    });
</script>

一個更簡單的方法來跟踪服務器上的用戶數是使用這個:

var numClients = io.sockets.clients().length;

但顯然這方面存在一些問題,因此您可能必須自己跟踪客戶數量。

房間和命名空間

隨著您的應用程序變得越來越複雜,您可能需要對 websocket 進行更多自定義,例如向特定用戶或一組用戶發送消息。或者,您可能需要在應用程序的不同部分之間嚴格分離邏輯。這就是房間和命名空間發揮作用的地方。

注意 :這些特性不是 websocket 協議的一部分,而是由 socket.io 加在上面的 .

默認情況下,socket.io 使用根命名空間(/ ) 發送和接收數據。以編程方式,您可以通過 io.sockets 訪問此命名空間 , 儘管它的許多方法在 io 上都有快捷方式 .所以這兩個調用是等價的:

io.sockets.emit('stats', { data: 'some data' });
io.emit('stats', { data: 'some data' });

要創建自己的命名空間,您只需執行以下操作:

var iosa = io.of('/stackabuse');
iosa.on('connection', function(socket){
    console.log('Connected to Stack Abuse namespace'):
});
iosa.emit('stats', { data: 'some data' });

此外,客戶端必須顯式連接到您的命名空間:

<script src="/socket.io/socket.io.js"></script>
<script>
    var socket = io('/stackabuse');
</script>

現在在這個命名空間內發送的任何數據都將與默認的 / 分開 命名空間,無論使用哪個通道。

更進一步,在每個命名空間中,您可以加入和離開“房間”。這些房間在命名空間之上提供了另一層分離,因為客戶端只能添加到服務器端的房間 ,它們還提供了一些額外的安全性。因此,如果您想確保用戶不會窺探某些數據,您可以使用房間來隱藏它。

要添加到房間,您必須 .join() 它:

io.on('connection', function(socket){
    socket.join('private-message-room');
});

然後從那裡您可以向屬於給定房間的每個人發送消息:

io.to('private-message-room').emit('some event');

最後,調用 .leave() 停止從房間接收事件消息:

socket.leave('private-message-room');

結論

這只是一個實現 websockets 協議的庫,還有更多的庫,它們都有自己獨特的功能和優勢。我建議您嘗試其他一些(例如 node-websockets),以便您了解那裡的內容。

只需幾行代碼,您就可以創建一些非常強大的應用程序,所以我很想看看您能想出什麼!

有一些很酷的想法,或者已經使用 websockets 創建了一些應用程序?請在評論中告訴我們!


Tutorial JavaScript 教程
  1. 提取 Javascript 數字的指數和尾數

  2. 如何檢測圖像加載失敗,如果失敗,嘗試重新加載直到成功?

  3. 比較 React Hooks 和 Vue Composition API

  4. 使用 Fetch 上傳多個文件

  5. 故事書插件 AngularJS (1.x)

  6. 使用 Google Sheets API 將基本格式和條件格式添加到電子表格

  7. 格式化日期對象的 JavaScript 錯誤

  1. 在 JavaScript Web 應用程序中逐步採用 PureScript

  2. 用於觸摸交互的 'mouseleave' 的 javascript 等效項

  3. 如何將 Epoch 中的日期轉換為 Javascript 中的 Y-m-d H:i:s?

  4. 如何在同一台機器上安裝多個節點版本

  5. HTML 表單數據到 JSON

  6. GraphQL + MongoDB。簡單的方法。

  7. 逆向工程——理解 JavaScript 中的 Promise

  1. DreamBox Learning 正在招聘高級軟件開發工程師

  2. 粘性標題導航菜單,當我向下滾動頁面時,頂部有間隙

  3. 使用 Google Cardboard 和 Three.js 將 VR 引入網絡

  4. 如何在 JavaScript 中解決根查找問題