教程推薦:node js教程、React教程、WebSocket教程
創(chuàng)新互聯(lián)專注于萬州網(wǎng)站建設(shè)服務(wù)及定制,我們擁有豐富的企業(yè)做網(wǎng)站經(jīng)驗(yàn)。 熱誠為您提供萬州營銷型網(wǎng)站建設(shè),萬州網(wǎng)站制作、萬州網(wǎng)頁設(shè)計(jì)、萬州網(wǎng)站官網(wǎng)定制、小程序制作服務(wù),打造萬州網(wǎng)絡(luò)公司原創(chuàng)品牌,更為您提供萬州網(wǎng)站排名全網(wǎng)營銷落地服務(wù)。
Web 為了支持客戶端和服務(wù)器之間的全雙工(或雙向)通信已經(jīng)走過了很長的路。這是 WebSocket 協(xié)議的主要目的:通過單個(gè) TCP 套接字連接在客戶端和服務(wù)器之間提供持久的實(shí)時(shí)通信。
WebSocket 協(xié)議只有兩個(gè)議程:1)打開握手,2)幫助數(shù)據(jù)傳輸。一旦服務(wù)器和客戶端握手成功,他們就可以隨意地以較少的開銷相互發(fā)送數(shù)據(jù)。
WebSocket 通信使用WS(端口80)或WSS(端口443)協(xié)議在單個(gè) TCP 套接字上進(jìn)行。根據(jù) Can I Use,撰寫本文時(shí)除了 Opera Mini 之外幾乎所有的瀏覽器支持 WebSockets 。
現(xiàn)狀從歷史上看,創(chuàng)建需要實(shí)時(shí)數(shù)據(jù)通訊(如游戲或聊天應(yīng)用程序)的 Web 應(yīng)用需要濫用 HTTP 協(xié)議來建立雙向數(shù)據(jù)傳輸。盡管有許多種方法用于實(shí)現(xiàn)實(shí)時(shí)功能,但沒有一種方法與 WebSockets 一樣高效。 HTTP 輪詢、HTTP流、Comet、SSE —— 它們都有自己的缺點(diǎn)。
HTTP 輪詢解決問題的第一個(gè)嘗試是定期輪詢服務(wù)器。 HTTP 長輪詢生命周期如下:
客戶端發(fā)出請求并一直等待響應(yīng)。服務(wù)器推遲響應(yīng),直到發(fā)生更改、更新或超時(shí)。請求保持“掛起”,直到服務(wù)器有東西返回客戶端。當(dāng)服務(wù)器端有一些更改或更新時(shí),它會(huì)將響應(yīng)發(fā)送回客戶端??蛻舳税l(fā)送新的長輪詢請求以偵聽下一組更改。長輪詢中存在很多漏洞 —— 標(biāo)頭開銷、延遲、超時(shí)、緩存等等。
HTTP 流式傳輸這種機(jī)制減少了網(wǎng)絡(luò)延遲的痛苦,因?yàn)槌跏颊埱鬅o限期地保持打開狀態(tài)。即使在服務(wù)器推送數(shù)據(jù)之后,請求也永遠(yuǎn)不會(huì)終止。 HTTP 流中的前三步生命周期方法與 HTTP 輪詢是相同的。
但是,當(dāng)響應(yīng)被發(fā)送回客戶端時(shí),請求永遠(yuǎn)不會(huì)終止,服務(wù)器保持連接打開狀態(tài),并在發(fā)生更改時(shí)發(fā)送新的更新。
服務(wù)器發(fā)送事件(SSE)使用 SSE,服務(wù)器將數(shù)據(jù)推送到客戶端。聊天或游戲應(yīng)用不能完全依賴 SSE。 SSE 的完美用例是類似 Facebook 的新聞 Feed:每當(dāng)有新帖發(fā)布時(shí),服務(wù)器會(huì)將它們推送到時(shí)間線。 SSE 通過傳統(tǒng) HTTP 發(fā)送,并且對打開的連接數(shù)有限制。
這些方法不僅效率低下,維護(hù)它們的代碼也使開發(fā)人員感到厭倦。
WebSocketWebSockets 旨在取代現(xiàn)有的雙向通信技術(shù)。當(dāng)涉及全雙工實(shí)時(shí)通信時(shí),上述現(xiàn)有方法既不可靠也不高效。
WebSockets 類似于 SSE,但在將消息從客戶端傳回服務(wù)器方面也很優(yōu)秀。由于數(shù)據(jù)是通過單個(gè) TCP 套接字連接提供的,因此連接限制不再是問題。
正如介紹中所提到的,WebSocket 協(xié)議只有兩個(gè)議程。讓我們看看 WebSockets 如何實(shí)現(xiàn)這些議程。為此我將分析一個(gè) Node.js 服務(wù)器并將其連接到使用 React.js 構(gòu)建的客戶端上。
議程1:WebSocket在服務(wù)器和客戶端之間建立握手在服務(wù)器級別創(chuàng)建握手我們可以用單個(gè)端口來分別提供 HTTP 服務(wù)和 WebSocket 服務(wù)。下面的代碼顯示了一個(gè)簡單的 HTTP 服務(wù)器的創(chuàng)建過程。一旦創(chuàng)建,我們會(huì)將 WebSocket 服務(wù)器綁定到 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 });
創(chuàng)建 WebSocket 服務(wù)器后,我們需要在接收來自客戶端的請求時(shí)接受握手。我將所有連接的客戶端作為對象保存在代碼中,并在收請從瀏覽器發(fā)來的求時(shí)使用唯一的用戶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)) });
那么,當(dāng)接受連接時(shí)會(huì)發(fā)生什么?
在發(fā)送常規(guī) HTTP 請求以建立連接時(shí),在請求頭中,客戶端發(fā)送 *Sec-WebSocket-Key*
。服務(wù)器對此值進(jìn)行編碼和散列,并添加預(yù)定義的 GUID。它回應(yīng)了服務(wù)器發(fā)送的握手中 *Sec-WebSocket-Accept*
中生成的值。
一旦請求在服務(wù)器中被接受(在必要驗(yàn)證之后),就完成了握手,其狀態(tài)代碼為 101
。如果在瀏覽器中看到除狀態(tài)碼 101
之外的任何內(nèi)容,則意味著 WebSocket 升級失敗,并且將遵循正常的 HTTP 語義。
*Sec-WebSocket-Accept*
頭字段指示服務(wù)器是否愿意接受連接。此外如果響應(yīng)缺少 *Upgrade*
頭字段,或者 *Upgrade*
不等于 websocket
,則表示 WebSocket 連接失敗。
成功的服務(wù)器握手如下所示:
HTTP GET ws://127.0.0.1:8000/ 101 Switching Protocols Connection: Upgrade Sec-WebSocket-Accept: Nn/XHq0wK1oO5RTtriEWwR4F7Zw= Upgrade: websocket在客戶端級別創(chuàng)建握手
在客戶端,我使用與服務(wù)器中的相同 WebSocket 包來建立與服務(wù)器的連接(Web IDL 中的 WebSocket API 正在由W3C 進(jìn)行標(biāo)準(zhǔn)化)。一旦服務(wù)器接受請求,我們將會(huì)在瀏覽器控制臺(tái)上看到 WebSocket Client Connected
。
這是創(chuàng)建與服務(wù)器的連接的初始腳手架:
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;
客戶端發(fā)送以下標(biāo)頭來建立握手:
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
現(xiàn)在客戶端和服務(wù)器通過相互握手進(jìn)行了連接,WebSocket 連接可以在接收消息時(shí)傳輸消息,從而實(shí)現(xiàn) WebSocket 協(xié)議的第二個(gè)議程。
議程2:實(shí)時(shí)信息傳輸我將編寫一個(gè)基本的實(shí)時(shí)文檔編輯器,用戶可以將它們連接在一起并編輯文檔。我跟蹤了兩個(gè)事件:
用戶活動(dòng):每次用戶加入或離開時(shí),我都會(huì)將消息廣播給所有連接其他的客戶端。內(nèi)容更改:每次修改編輯器中的內(nèi)容時(shí),都會(huì)向所有連接的其他客戶端廣播。該協(xié)議允許我們用二進(jìn)制數(shù)據(jù)或 UTF-8 發(fā)送和接收消息(注意:傳輸和轉(zhuǎn)換 UTF-8 的開銷較?。?。
只要我們對套接字事件onopen
、onclose
和 onmessage
有了充分的了解,理解和實(shí)現(xiàn) WebSockets 就非常簡單??蛻舳撕头?wù)器端的術(shù)語相同。
在客戶端,當(dāng)新用戶加入或內(nèi)容更改時(shí),我們用 client.send
向服務(wù)器發(fā)消息,以將新信息提供給服務(wù)器。
/* 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 })); };
我們跟蹤的事件是:用戶加入和內(nèi)容更改。
從服務(wù)器接收消息非常簡單:
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 }); }; }在服務(wù)器端發(fā)送和偵聽消息
在服務(wù)器中,我們只需捕獲傳入的消息并將其廣播到連接到 WebSocket 的所有客戶端。這是臭名昭著的 Socket.IO 和 WebSocket 之間的差異之一:當(dāng)我們使用 WebSockets 時(shí),我們需要手動(dòng)將消息發(fā)送給所有客戶端。 Socket.IO 是一個(gè)成熟的庫,所以它自己來處理。
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)); } });
將消息廣播到所有連接的客戶端。
瀏覽器關(guān)閉后會(huì)發(fā)生什么?在這種情況下,WebSocket調(diào)用 close
事件,它允許我們編寫終止當(dāng)前用戶連接的邏輯。在我的代碼中,當(dāng)用戶離開文檔時(shí),會(huì)向其余用戶廣播消息:
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)); });
該應(yīng)用程序的源代碼位于GitHub上的 repo 中。
結(jié)論WebSockets 是在應(yīng)用中實(shí)現(xiàn)實(shí)時(shí)功能的最有趣和最方便的方法之一。它為我們提供了能夠充分利用全雙工通信的靈活性。我強(qiáng)烈建議在嘗試使用 Socket.IO 和其他可用庫之前先試試 WebSockets。
編碼快樂!
更多編程相關(guān)知識,請?jiān)L問:編程教學(xué)?。?
網(wǎng)頁名稱:Node和React中如何進(jìn)行實(shí)時(shí)通信?
標(biāo)題網(wǎng)址:http://www.rwnh.cn/article24/cpogce.html
成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供手機(jī)網(wǎng)站建設(shè)、網(wǎng)站收錄、企業(yè)建站、定制開發(fā)、ChatGPT、做網(wǎng)站
聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請盡快告知,我們將會(huì)在第一時(shí)間刪除。文章觀點(diǎn)不代表本網(wǎng)站立場,如需處理請聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時(shí)需注明來源: 創(chuàng)新互聯(lián)