用 PeerJS 馴服 WebRTC:製作一個簡單的 P2P 網頁遊戲
已發表: 2022-03-11WebRTC 是一種實現 Web 瀏覽器之間實時通信的技術。 比較新,API定義還算草稿。 再加上並非所有主要網絡瀏覽器都支持 WebRTC(在支持的瀏覽器中,有些瀏覽器並不支持該技術的所有功能),這使得將 WebRTC 用於任何關鍵任務應用程序變得相對困難. 或者你會這麼想!
自 2011 年 5 月 Google 首次推出以來,WebRTC 已被用於許多現代 Web 應用程序中。 作為許多現代 Web 瀏覽器的核心功能,Web 應用程序可以無縫地利用該技術以多種方式提供改進的用戶體驗。 視頻流或會議應用程序不需要臃腫的瀏覽器插件,並且可以利用對等 (P2P) 網絡(而不是通過某些服務器傳輸所有數據)只是所有令人驚奇的事情的一部分可以通過 WebRTC 實現。
在本文中,我們將了解如何使用 WebRTC 製作連接四的簡單 P2P 網頁遊戲。 為了解決 WebRTC 的各種粗糙邊緣和實現差異,我們將使用一個驚人的 JavaScript 庫:PeerJS。
WebRTC 上的數據
在我們開始之前,重要的是要了解 WebRTC 不僅僅是傳輸音頻和視頻流。 它還提供對 P2P 數據通道的支持。 這些渠道有兩種變體:可靠和不可靠。 正如人們可能猜到的那樣,可靠的數據通道保證消息被傳遞並按順序傳遞,而不可靠的通道不提供這樣的保證。
此外,WebRTC 數據通道不需要特殊的基礎設施設置,除了典型的 WebRTC 對等連接所需要的:一個信號服務器來協調對等之間的連接,一個 STUN 服務器來確定對等的公共身份,以及一個可選的 TURN 服務器如果無法建立對等點之間的直接連接(例如,當兩個對等點都在 NAT 之後),則在對等點之間路由消息。 如果這些首字母縮略詞聽起來很熟悉,那是因為 WebRTC 盡可能地重新利用了現有技術。
這為 WebRTC 的更多用例打開了大門,包括但不限於多人遊戲、內容交付和文件共享。 同樣,所有這些都不需要任何中間服務器,因此延遲更低。
在我們的簡單網頁遊戲中,我們將使用兩個網頁瀏覽器之間的數據通道來來回交流玩家移動。
認識 PeerJS
PeerJS 在您的瀏覽器中實現 WebRTC,並圍繞它包裝了一個簡單、一致且優雅的 API。 它填補了早期瀏覽器的 WebRTC 實現中的各種漏洞。 例如,在 Chrome 30 或更早版本中,只有不可靠的數據通道可用。 PeerJS,如果配置為使用可靠的數據通道,將為那些舊瀏覽器使用 shim。 儘管這不會像可靠通道的本機實現那樣高效,但它仍然可以工作。
使用 PeerJS,識別對等點更加簡單。 每個對等點都只使用一個 ID 來標識。 對等方可以選擇自己的字符串,或者讓服務器生成一個字符串。 儘管 WebRTC 承諾點對點通信,但您仍然需要一個服務器來充當連接代理並處理信號。 PeerJS 提供了這個連接代理服務器 PeerJS Server(用 Node.js 編寫)的開源實現,以防您不想使用他們的雲託管版本(現在是免費的,但有一些限制)。
連接四去 P2P
現在我們有了使用 WebRTC 的信心來源,即 PeerJS,讓我們從創建一個簡單的 Node.js/Express 應用程序開始。
npm init npm install express --save npm install jade --save npm install peer --save
我們將僅使用它來託管 PeerJS 服務器,並提供頁面和前端資產。 我們只需要提供一個頁面,這將包含兩個部分:一個普通的主菜單和一個 7×6 連接四個網格。
PeerJS 服務器
託管我們自己的 PeerJS 服務器非常簡單。 GitHub 上的官方倉庫甚至有一個一鍵按鈕,可以將 PeerJS Server 的實例部署到 Heroku。
在我們的例子中,我們只想在我們的 Node.js 應用程序中創建一個ExpressPeerServer實例,並在“/peerjs”處提供它:
var express = require('express') var app = express() // … Configure Express, and register necessary route handlers srv = app.listen(process.env.PORT) app.use('/peerjs', require('peer').ExpressPeerServer(srv, { debug: true }))
PeerJS 客戶端
隨著 PeerJS 服務器的啟動和運行,我們進入客戶端。 如前所述,PeerJS 使用唯一 ID 標識對等點。 這些 ID 可以由 PeerServer 自動為每個 peer 生成,或者我們可以在實例化Peer對象時為每個 peer 選擇一個。
var peer = new Peer(id, options)
在這裡,如果我們希望服務器為我們生成一個,則可以完全省略id 。 在我們的例子中,這就是我們想要做的。 PeerServer 將確保它給出的 ID 是唯一的。 第二個參數options通常是一個包含密鑰的對象(API 密鑰,如果您使用雲託管的 PeerServer,或者如果您自己託管 PeerServer,則為host 、 port 、 path等)。
var peer = new Peer({ host: location.hostname, port: location.port || (location.protocol === 'https:' ? 443 : 80), path: '/peerjs' })
為了在兩個 PeerJS 對等點之間建立連接,其中一個對等點必須知道另一個對等點的 ID。 為了簡單起見,在我們通過 WebRTC 實現 Connect Four 的過程中,我們將要求開始遊戲的玩家與他的對手共享他的對等 ID。 在已知目標對等 ID 的情況下,只需簡單調用peer.connect(destId) 即可:
var conn = peer.connect(destId)
peer.connect(destId)返回的Peer對象和DataConnection對像都會發出一些非常有用的事件,值得傾聽。 出於本教程的目的,我們對DataConnection對象的“數據”事件和兩個對象的“錯誤”事件特別感興趣。

為了向連接的另一端發送數據,只需調用conn.send(data) :
conn.send('hello')
雖然在這裡我們的需求有點過頭了,但 PeerJS 在將數據編碼為 BinaryPack 格式之後在對等點之間傳輸數據。 這允許對等點通信字符串、數字、數組、對象甚至 blob。
要接收傳入數據,只需在conn上偵聽 'data' 事件:
conn.on('data', function(data) { // data === 'hello' })
這幾乎就是我們所需要的!
遊戲邏輯
第一個開始遊戲的玩家會看到由 PeerJS 生成的同伴 ID,他們可以與對手共享。 一旦對手使用第一個玩家的對等 ID 加入遊戲,則允許第一個玩家移動。
連接四,作為一個簡單的規則和機制的遊戲,只有一種類型的移動:每個玩家依次必須選擇一列並將一個圓盤放入其中。 這意味著所有對等點需要通信的是當前玩家選擇將他的光盤放入其中的列號。我們將將此信息作為包含兩個元素的數組傳輸:字符串“移動”和數字 - 0-基於左側列的索引。
每次玩家點擊列時:
if(!turn) { // it is not the current player's turn return } var i // i = chosen column index if(grid[i].length == 6) { // the column doesn't have any more space available return } // track player's move locally grid[i].push(peerId) // end current player's turn turn = false conn.send(['move', i])
將此移動數據發送給對手後,我們在本地更新遊戲狀態。 這包括確定當前玩家是否贏了,或者遊戲是否以平局結束。
在此移動數據的接收端:
if(turn) { // ignore incoming move data when it is the current player's turn return } var i = data[1] if(grid[i].length == 6) { // ignore incoming move data when it is invalid return } // track opponent's move locally grid[i].push(opponent.peerId) // activate current player's turn turn = true
當然,在這之後我們會在本地更新遊戲狀態,確定對手是否獲勝或遊戲是否以平局結束。
請注意我們需要如何對傳入數據執行完整性檢查。 這一點很重要,因為對於基於 WebRTC 的遊戲,我們沒有中間服務器和基於服務器的遊戲邏輯來驗證移動數據。
為了使代碼片段保持簡單,更新 UI 的代碼行已被省略。 您可以在此處找到客戶端 JavaScript 的完整源代碼。
連接一切
為了將這一切結合起來,我們創建了一個包含兩個部分的簡單頁面。 在頁面加載時,顯示包含主菜單的部分,包含遊戲網格的部分保持隱藏。
section#menu div.animated.bounceIn div h1 Connect Four br div.no-support() div.alert.alert-warning p Unfortunately, your web browser does not <a href="http://iswebrtcreadyyet.com">support WebRTC</a> div a.btn.btn-primary.btn-lg(href='#start') Start | a.btn.btn-default.btn-lg(href='#join') Join section#game() div div h1 Connect Four br table.table.grid tbody for i in [0, 1, 2, 3, 4, 5] tr for j in [0, 1, 2, 3, 4, 5, 6] td div.slot br div.alert.alert-info p
使這些 DOM 元素看起來很漂亮超出了本教程的範圍。 因此,我們將求助於我們值得信賴的伙伴 Bootstrap 並對其進行一些簡單的造型。
當第一個玩家點擊“開始”按鈕時,遊戲網格與玩家的同伴 ID 一起顯示。 然後玩家可以與他們的對手共享這個對等 ID。
第二個玩家可以點擊然後點擊“加入”按鈕,輸入第一個玩家的peer ID,然後開始遊戲。
試一試
您可以在 https://arteegee.herokuapp.com 試用此示例應用程序。
或者,您可以從 GitHub 克隆存儲庫,安裝 NPM 依賴項,然後在本地試用:
git clone https://github.com/hjr265/arteegee.git cd arteegee npm install PORT=5000 npm start
服務器運行後,您可以將 Web 瀏覽器指向 http://localhost:5000,從一個選項卡開始遊戲,然後使用對等 ID 從另一個選項卡(甚至是支持 WebRTC 的不同 Web 瀏覽器)加入。
您可以打開 Web 瀏覽器的控制台以查看一些調試信息,如在此示例應用程序中,PeerJS 客戶端已配置為執行詳細日誌記錄。
但這對我不起作用!
此遊戲可能無法在您的計算機上運行的主要原因有兩個。
您使用的 Web 瀏覽器可能還不支持必要的 WebRTC API。 如果是這種情況,您可能需要嘗試其他瀏覽器 - 支持 WebRTC 和數據通道的瀏覽器。
如果您使用支持 WebRTC 的現代 Web 瀏覽器,那麼您有可能落後於 WebRTC 無法滲透的某些網絡基礎設施。 理想情況下,這個問題可以通過 TURN 服務器輕鬆解決,但由於示例應用程序沒有使用一個,因此當您和您的對手都在 NAT 之後時,它將無法工作。
結論
WebRTC 是一項新技術,其實現還遠未成熟。 這些通常會給開發人員帶來一些獨特的挑戰。 然而,隨著像 PeerJS 這樣巧妙地抽像出粗糙的原始 API 的庫的可用性,該技術已經變得非常容易使用。
我希望這個構建基於 PeerJS 的遊戲的簡短教程能夠幫助您開始使用 WebRTC 並構建一些令人驚嘆的實時點對點 Web 應用程序。