教學推薦:node js教學、React教學、WebSocket教學
Web 為了支援客戶端和伺服器之間的全雙工(或雙向)通訊已經走過了很長的路。這是 WebSocket 協定的主要目的:透過單一 TCP 套接字連接在客戶端和伺服器之間提供持久的即時通訊。
WebSocket 協定只有兩個議程:1)開啟握手,2)幫助資料傳輸。一旦伺服器和客戶端握手成功,他們就可以隨意地以較少的開銷相互發送資料。
WebSocket 通訊使用WS(連接埠80)或WSS(連接埠443)協定在單一 TCP 套接字上進行。根據 Can I Use,撰寫本文時除了 Opera Mini 之外幾乎所有的瀏覽器支援 WebSockets 。
現況
從歷史上看,創建需要即時資料通訊(如遊戲或聊天應用程式)的 Web 應用程式需要濫用 HTTP 協定來建立雙向資料傳輸。儘管有許多種方法用於實現即時功能,但沒有一種方法與 WebSockets 一樣有效率。 HTTP 輪詢、HTTP流、Comet、SSE —— 它們都有自己的缺點。
HTTP 輪詢
解決問題的第一個嘗試是定期輪詢伺服器。 HTTP 長輪詢生命週期如下:
- 客戶端發出請求並一直等待回應。
- 伺服器會延遲回應,直到發生變更、更新或逾時。請求保持“掛起”,直到伺服器有東西返回客戶端。
- 當伺服器端有一些變更或更新時,它會將回應傳送回客戶端。
- 客戶端發送新的長輪詢請求以偵聽下一組變更。
長輪詢中存在著許多漏洞 —— 標頭開銷、延遲、逾時、快取等等。
HTTP 串流
這種機制減少了網路延遲的痛苦,因為初始請求無限期地保持開啟。即使在伺服器推送資料之後,請求也永遠不會終止。 HTTP 流中的前三步驟生命週期方法與 HTTP 輪詢是相同的。
但是,當回應傳送回客戶端時,請求永遠不會終止,伺服器保持連線開啟狀態,並在發生變更時發送新的更新。
伺服器傳送事件(SSE)
使用 SSE,伺服器將資料推送到客戶端。聊天或遊戲應用程式不能完全依賴 SSE。 SSE 的完美用例是類似 Facebook 的新聞 Feed:每當有新貼文發佈時,伺服器會將它們推送到時間軸。 SSE 透過傳統 HTTP 傳送,並且對開啟的連線數有限制。
這些方法不僅效率低下,維護它們的程式碼也使開發人員感到厭倦。
WebSocket
WebSockets 旨在取代現有的雙向通訊技術。當涉及全雙工即時通訊時,上述現有方法既不可靠也不高效。
WebSockets 類似於 SSE,但在將訊息從客戶端傳回伺服器方面也很優秀。由於資料是透過單一 TCP 套接字連接提供的,因此連線限制不再是問題。
實戰教學
如同介紹中所提到的,WebSocket 協定只有兩個議程。讓我們看看 WebSockets 如何實現這些議程。為此我將分析一個 Node.js 伺服器並將其連接到使用 React.js 建置的客戶端。
議程1:WebSocket在伺服器和客戶端之間建立握手
在伺服器層級建立握手
我們可以用單一連接埠分別提供 HTTP 服務和 WebSocket 服務。下面的程式碼顯示了一個簡單的 HTTP 伺服器的建立過程。一旦創建,我們會將 WebSocket 伺服器綁定到 HTTP 連接埠:
const webSocketsServerPort = 8000; const webSocketServer = require('websocket').server; const http = require('http'); // Spinning the http server and the websocket server. const server = http.createServer(); server.listen(webSocketsServerPort); const wsServer = new webSocketServer({ httpServer: server });
建立 WebSocket 伺服器後,我們需要在接收來自客戶端的請求時接受握手。我將所有連線的客戶端作為物件保存在程式碼中,並在收請從瀏覽器發送的求時使用唯一的使用者ID。
// I'm maintaining all active connections in this object const clients = {}; // This code generates unique userid for everyuser. const getUniqueID = () => { const s4 = () => Math.floor((1 + Math.random()) * 0x10000).toString(16).substring(1); return s4() + s4() + '-' + s4(); }; wsServer.on('request', function(request) { var userID = getUniqueID(); console.log((new Date()) + ' Recieved a new connection from origin ' + request.origin + '.'); // You can rewrite this part of the code to accept only the requests from allowed origin const connection = request.accept(null, request.origin); clients[userID] = connection; console.log('connected: ' + userID + ' in ' + Object.getOwnPropertyNames(clients)) });
那麼,當接受連線時會發生什麼事?
在傳送常規 HTTP 請求以建立連線時,在請求頭中,用戶端會傳送 *Sec-WebSocket-Key*
。伺服器對此值進行編碼和雜湊,並新增預先定義的 GUID。它回應了伺服器發送的握手中 *Sec-WebSocket-Accept*
中產生的值。
一旦請求在伺服器中被接受(在必要驗證之後),就完成了握手,其狀態代碼為 101
。如果在瀏覽器中看到除狀態碼 101
之外的任何內容,則表示 WebSocket 升級失敗,並且將遵循正常的 HTTP 語意。
*Sec-WebSocket-Accept*
头字段指示服务器是否愿意接受连接。此外如果响应缺少 *Upgrade*
头字段,或者 *Upgrade*
不等于 websocket
,则表示 WebSocket 连接失败。
成功的服务器握手如下所示:
HTTP GET ws://127.0.0.1:8000/ 101 Switching Protocols Connection: Upgrade Sec-WebSocket-Accept: Nn/XHq0wK1oO5RTtriEWwR4F7Zw= Upgrade: websocket
在客户端级别创建握手
在客户端,我使用与服务器中的相同 WebSocket 包来建立与服务器的连接(Web IDL 中的 WebSocket API 正在由W3C 进行标准化)。一旦服务器接受请求,我们将会在浏览器控制台上看到 WebSocket Client Connected
。
这是创建与服务器的连接的初始脚手架:
import React, { Component } from 'react'; import { w3cwebsocket as W3CWebSocket } from "websocket"; const client = new W3CWebSocket('ws://127.0.0.1:8000'); class App extends Component { componentWillMount() { client.onopen = () => { console.log('WebSocket Client Connected'); }; client.onmessage = (message) => { console.log(message); }; } render() { return ( <div> Practical Intro To WebSockets. </div> ); } } export default App;
客户端发送以下标头来建立握手:
HTTP GET ws://127.0.0.1:8000/ 101 Switching Protocols Upgrade: websocket Connection: Upgrade Sec-WebSocket-Key: vISxbQhM64Vzcr/CD7WHnw== Origin: http://localhost:3000 Sec-WebSocket-Version: 13
现在客户端和服务器通过相互握手进行了连接,WebSocket 连接可以在接收消息时传输消息,从而实现 WebSocket 协议的第二个议程。
议程2:实时信息传输
我将编写一个基本的实时文档编辑器,用户可以将它们连接在一起并编辑文档。我跟踪了两个事件:
- 用户活动:每次用户加入或离开时,我都会将消息广播给所有连接其他的客户端。
- 内容更改:每次修改编辑器中的内容时,都会向所有连接的其他客户端广播。
该协议允许我们用二进制数据或 UTF-8 发送和接收消息(注意:传输和转换 UTF-8 的开销较小)。
只要我们对套接字事件onopen
、onclose
和 onmessage
有了充分的了解,理解和实现 WebSockets 就非常简单。客户端和服务器端的术语相同。
在客户端发送和接收消息
在客户端,当新用户加入或内容更改时,我们用 client.send
向服务器发消息,以将新信息提供给服务器。
/* When a user joins, I notify the server that a new user has joined to edit the document. */ logInUser = () => { const username = this.username.value; if (username.trim()) { const data = { username }; this.setState({ ...data }, () => { client.send(JSON.stringify({ ...data, type: "userevent" })); }); } } /* When content changes, we send the current content of the editor to the server. */ onEditorStateChange = (text) => { client.send(JSON.stringify({ type: "contentchange", username: this.state.username, content: text })); };
我们跟踪的事件是:用户加入和内容更改。
从服务器接收消息非常简单:
componentWillMount() { client.onopen = () => { console.log('WebSocket Client Connected'); }; client.onmessage = (message) => { const dataFromServer = JSON.parse(message.data); const stateToChange = {}; if (dataFromServer.type === "userevent") { stateToChange.currentUsers = Object.values(dataFromServer.data.users); } else if (dataFromServer.type === "contentchange") { stateToChange.text = dataFromServer.data.editorContent || contentDefaultMessage; } stateToChange.userActivity = dataFromServer.data.userActivity; this.setState({ ...stateToChange }); }; }
在服务器端发送和侦听消息
在服务器中,我们只需捕获传入的消息并将其广播到连接到 WebSocket 的所有客户端。这是臭名昭着的 Socket.IO 和 WebSocket 之间的差异之一:当我们使用 WebSockets 时,我们需要手动将消息发送给所有客户端。 Socket.IO 是一个成熟的库,所以它自己来处理。
const sendMessage = (json) => { // We are sending the current data to all connected clients Object.keys(clients).map((client) => { clients[client].sendUTF(json); }); } connection.on('message', function(message) { if (message.type === 'utf8') { const dataFromClient = JSON.parse(message.utf8Data); const json = { type: dataFromClient.type }; if (dataFromClient.type === typesDef.USER_EVENT) { users[userID] = dataFromClient; userActivity.push(`${dataFromClient.username} joined to edit the document`); json.data = { users, userActivity }; } else if (dataFromClient.type === typesDef.CONTENT_CHANGE) { editorContent = dataFromClient.content; json.data = { editorContent, userActivity }; } sendMessage(JSON.stringify(json)); } });
将消息广播到所有连接的客户端。
浏览器关闭后会发生什么?
在这种情况下,WebSocket调用 close
事件,它允许我们编写终止当前用户连接的逻辑。在我的代码中,当用户离开文档时,会向其余用户广播消息:
connection.on('close', function(connection) { console.log((new Date()) + " Peer " + userID + " disconnected."); const json = { type: typesDef.USER_EVENT }; userActivity.push(`${users[userID].username} left the document`); json.data = { users, userActivity }; delete clients[userID]; delete users[userID]; sendMessage(JSON.stringify(json)); });
该应用程序的源代码位于GitHub上的 repo 中。
结论
WebSockets 是在应用中实现实时功能的最有趣和最方便的方法之一。它为我们提供了能够充分利用全双工通信的灵活性。我强烈建议在尝试使用 Socket.IO 和其他可用库之前先试试 WebSockets。
编码快乐!
更多编程相关知识,请访问:编程教学!!
以上是Node和React中如何進行即時通訊?的詳細內容。更多資訊請關注PHP中文網其他相關文章!

JavaScript是現代Web開發的核心語言,因其多樣性和靈活性而廣泛應用。 1)前端開發:通過DOM操作和現代框架(如React、Vue.js、Angular)構建動態網頁和單頁面應用。 2)服務器端開發:Node.js利用非阻塞I/O模型處理高並發和實時應用。 3)移動和桌面應用開發:通過ReactNative和Electron實現跨平台開發,提高開發效率。

JavaScript的最新趨勢包括TypeScript的崛起、現代框架和庫的流行以及WebAssembly的應用。未來前景涵蓋更強大的類型系統、服務器端JavaScript的發展、人工智能和機器學習的擴展以及物聯網和邊緣計算的潛力。

JavaScript是現代Web開發的基石,它的主要功能包括事件驅動編程、動態內容生成和異步編程。 1)事件驅動編程允許網頁根據用戶操作動態變化。 2)動態內容生成使得頁面內容可以根據條件調整。 3)異步編程確保用戶界面不被阻塞。 JavaScript廣泛應用於網頁交互、單頁面應用和服務器端開發,極大地提升了用戶體驗和跨平台開發的靈活性。

Python更适合数据科学和机器学习,JavaScript更适合前端和全栈开发。1.Python以简洁语法和丰富库生态著称,适用于数据分析和Web开发。2.JavaScript是前端开发核心,Node.js支持服务器端编程,适用于全栈开发。

JavaScript不需要安裝,因為它已內置於現代瀏覽器中。你只需文本編輯器和瀏覽器即可開始使用。 1)在瀏覽器環境中,通過標籤嵌入HTML文件中運行。 2)在Node.js環境中,下載並安裝Node.js後,通過命令行運行JavaScript文件。

如何在Quartz中提前發送任務通知在使用Quartz定時器進行任務調度時,任務的執行時間是由cron表達式設定的。現�...

在JavaScript中如何獲取原型鏈上函數的參數在JavaScript編程中,理解和操作原型鏈上的函數參數是常見且重要的任�...

在微信小程序web-view中使用Vue.js動態style位移失效的原因分析在使用Vue.js...


熱AI工具

Undresser.AI Undress
人工智慧驅動的應用程序,用於創建逼真的裸體照片

AI Clothes Remover
用於從照片中去除衣服的線上人工智慧工具。

Undress AI Tool
免費脫衣圖片

Clothoff.io
AI脫衣器

AI Hentai Generator
免費產生 AI 無盡。

熱門文章

熱工具

mPDF
mPDF是一個PHP庫,可以從UTF-8編碼的HTML產生PDF檔案。原作者Ian Back編寫mPDF以從他的網站上「即時」輸出PDF文件,並處理不同的語言。與原始腳本如HTML2FPDF相比,它的速度較慢,並且在使用Unicode字體時產生的檔案較大,但支援CSS樣式等,並進行了大量增強。支援幾乎所有語言,包括RTL(阿拉伯語和希伯來語)和CJK(中日韓)。支援嵌套的區塊級元素(如P、DIV),

SublimeText3 Linux新版
SublimeText3 Linux最新版

Dreamweaver Mac版
視覺化網頁開發工具

SublimeText3 英文版
推薦:為Win版本,支援程式碼提示!

DVWA
Damn Vulnerable Web App (DVWA) 是一個PHP/MySQL的Web應用程序,非常容易受到攻擊。它的主要目標是成為安全專業人員在合法環境中測試自己的技能和工具的輔助工具,幫助Web開發人員更好地理解保護網路應用程式的過程,並幫助教師/學生在課堂環境中教授/學習Web應用程式安全性。 DVWA的目標是透過簡單直接的介面練習一些最常見的Web漏洞,難度各不相同。請注意,該軟體中