<ruby id="bdb3f"></ruby>

    <p id="bdb3f"><cite id="bdb3f"></cite></p>

      <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
        <p id="bdb3f"><cite id="bdb3f"></cite></p>

          <pre id="bdb3f"></pre>
          <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

          <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
          <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

          <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                <ruby id="bdb3f"></ruby>

                合規國際互聯網加速 OSASE為企業客戶提供高速穩定SD-WAN國際加速解決方案。 廣告
                &emsp;&emsp;這個[Socket.IO](https://socket.io/)是一個建立在 WebSocket 協議之上的庫,可以在客戶端和服務器之間實現低延遲、雙向和基于事件的通信。 :-: ![](https://img.kancloud.cn/fe/96/fe9652857d1e096233080d3180029900_840x131.png =600x) &emsp;&emsp;并且提供額外的保證,例如回退到 HTTP 長輪詢、自動重連、數據包緩沖、多路復用等。 &emsp;&emsp;這個[WebSocket](https://zh.wikipedia.org/wiki/WebSocket)是一種基于 TCP 協議在服務器和瀏覽器之間提供全雙工和低延遲通道的通信協議。 &emsp;&emsp;注意,Socket.IO 不是 WebSocket 的實現。盡管 Socket.IO 確實在可能的情況下使用 WebSocket 進行傳輸,但它為每個數據包添加了額外的元數據。 &emsp;&emsp;這就是為什么 WebSocket 客戶端將無法成功連接到 Socket.IO 服務器,而 Socket.IO 客戶端也將無法連接到普通的 WebSocket 服務器。 &emsp;&emsp;如果需要一個普通的 WebSocket 服務器,可以使用[ws](https://github.com/websockets/ws)或[μWebSockets.js](https://github.com/uNetworking/uWebSockets.js)。 &emsp;&emsp;在 Socket.IO 的底層依賴[Engine.IO](https://github.com/socketio/engine.io)引擎,它是跨瀏覽器/跨設備雙向通信層的實現,可處理各種傳輸、[升級機制](https://developer.mozilla.org/zh-CN/docs/Web/HTTP/Protocol_upgrade_mechanism)和斷線檢測等。 &emsp;&emsp;剛剛所說的自動重連、數據包緩沖、多路復用等附加功能都是 Engine.IO 引擎提供的能力。 &emsp;&emsp;本系列所有的示例源碼都已上傳至Github,[點擊此處](https://github.com/pwstrick/node)獲取。 ## 一、廣播 &emsp;&emsp;現在來建立一個提供表單和消息列表的簡單 HTML 網頁,用 Socket.IO 廣播消息(如下圖所示),并且可以在頁面中呈現消息內容。 :-: ![](https://img.kancloud.cn/80/71/8071c4a28f950597dcba222bc9eea54e_688x331.png =500x) **1) HTTP 服務器** &emsp;&emsp;首先安裝 socket.io 包:npm install socket.io。 &emsp;&emsp;然后創建一個[HTTP 服務器](https://www.cnblogs.com/strick/p/16243384.html),用于接收 HTML 和 JavaScript 文件的請求,內部實現了個簡單的路由。 &emsp;&emsp;其中[URL](https://nodejs.org/dist/latest-v18.x/docs/api/url.html)實例用于解析請求地址,最終響應的內容是通過[fs.readFileSync()](https://www.cnblogs.com/strick/p/16252310.html)同步讀取到的。 &emsp;&emsp;index.html 文件的內容會在后文給出,socket.io.js 是從 node\_modules/socket.io/client-dist/socket.io.js 目錄中復制過來的。 ~~~ const http = require('http'); const fs = require('fs'); // HTTP服務器 const server = http.createServer((req, res) => { // 實例化 URL 類 const url = new URL(req.url, 'http://localhost:1234'); const { pathname } = url; // 路由 if(pathname === '/') { res.writeHead(200, { 'Content-Type': 'text/html' }); res.end(fs.readFileSync('./index.html')); }else if(pathname === '/socket.io.js') { res.writeHead(200, { 'Content-Type': 'application/javascript' }); res.end(fs.readFileSync('../socket.io.js')); } }); // 監控端口 server.listen(1234); ~~~ **2)Socket.IO 服務器** &emsp;&emsp;接著是創建 Socket.IO 服務器,其中 socket.id 是每個新連接都會被分配到的一個隨機的 20 個字符的標識符,此標識符與客戶端的值同步。 &emsp;&emsp;connection 是建立連接時的事件,disconnect 是斷開連接時的事件,chat message 是注冊的接收消息的自定義事件。 ~~~ const { Server } = require("socket.io"); const io = new Server(server); io.on('connection', (socket) => { console.log('id', socket.id); // socket.broadcast.emit('hi'); // 廣播給其他人,除了自己 console.log('a user connected'); // 注冊斷開連接事件 socket.on('disconnect', () => { console.log('user disconnected'); }); // 注冊接收消息事件 socket.on('chat message', (msg) => { console.log('message: ' + msg); // 觸發事件 io.emit('chat message', msg); }); }); ~~~ **3)廣播頁面** &emsp;&emsp;在廣播頁面中,先給出 HTML 結構和 CSS 樣式,在表單中有一個按鈕和文本框,如下圖所示。 ~~~html <!DOCTYPE html> <html> <head> <title>Socket.IO broadcast</title> <style> body { margin: 0; padding-bottom: 3rem; font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif; } #form { background: rgba(0, 0, 0, 0.15); padding: 0.25rem; position: fixed; bottom: 0; left: 0; right: 0; display: flex; height: 3rem; box-sizing: border-box; backdrop-filter: blur(10px); } #input { border: none; padding: 0 1rem; flex-grow: 1; border-radius: 2rem; margin: 0.25rem; } #input:focus { outline: none; } #form > button { background: #333; border: none; padding: 0 1rem; margin: 0.25rem; border-radius: 3px; outline: none; color: #fff; } #messages { list-style-type: none; margin: 0; padding: 0; } #messages > li { padding: 0.5rem 1rem; } #messages > li:nth-child(odd) { background: #efefef; } </style> </head> <body> <ul id="messages"></ul> <form id="form" action=""> <input id="input" autocomplete="off" /><button>Send</button> </form> <script src="../socket.io.js"></script> </body> </html> ~~~ :-: ![](https://img.kancloud.cn/1c/42/1c426a250bf38c4e40f902b9edf579af_1392x90.png =600x) &emsp;&emsp;在頁面的內嵌腳本中,先初始化 socket,io() 中的協議既可以是 http 也可以是 ws。 &emsp;&emsp;其中 WS 是 WebSocket 協議的縮寫,WSS(Web Socket Secure)是 WebSocket 的加密版本。WS 一般默認是 80 端口,而 WSS 默認是 443 端口。 ~~~ var socket = io("ws://localhost:1234"); ~~~ &emsp;&emsp;然后是注冊表單提交事件,在文本框中輸入內容后,觸發 chat message 事件發送消息到服務器中,服務器情況如下圖所示。 ~~~ var messages = document.getElementById("messages"); var form = document.getElementById("form"); var input = document.getElementById("input"); // 注冊表單提交事件 form.addEventListener("submit", function (e) { e.preventDefault(); if (input.value) { socket.emit("chat message", input.value); input.value = ""; } }); ~~~ :-: ![](https://img.kancloud.cn/b9/b3/b9b3bfde7ba98f79e3537c424b424566_908x114.png =600x) &emsp;&emsp;最后注冊 chat message 事件,和服務器中的事件同名,在接收到從服務器傳回的消息時,就在頁面中增加一欄消息(如下圖所示),類似于聊天記錄。 ~~~ socket.on("chat message", function (msg) { var item = document.createElement("li"); item.textContent = msg; messages.appendChild(item); window.scrollTo(0, document.body.scrollHeight); }); ~~~ :-: ![](https://img.kancloud.cn/38/b4/38b4004ed0af801da35e3651308d24f7_1132x522.png =600x) &emsp;&emsp;客戶端中的 socket 實例有 3 個保留事件,connect、connect\_error 和 disconnect。 &emsp;&emsp;其中 connect\_error 會在底層連接失敗或中間件拒絕連接時觸發。 &emsp;&emsp;下面是一張客戶端 socket 連接的生命周期圖,在建立連接時會分兩種情況,在斷開連接時還會自動重連。 :-: ![](https://img.kancloud.cn/04/2c/042c51310d2d8efeca3cefd2a91dc588_1242x1266.png =800x) **4)請求頭和消息** &emsp;&emsp;下圖是一個請求頭,狀態碼是 101 表示可以使用新協議,Connection: Upgrade 指示這是一個升級請求,Upgrade 指定 websocket 協議。 &emsp;&emsp;一旦這次升級完成后,連接就變成了雙向管道。sid 參數表示一個會話 ID。 :-: ![](https://img.kancloud.cn/66/d9/66d9250c0b92e8497cd8bd6e639c96b5_1400x370.png =800x) &emsp;&emsp;下圖一系列的消息,每條消息的開頭都是 1 到 2 個數字,它們都有各自的含義。 &emsp;&emsp;第四條是發送的消息,第五條是接收的消息。 :-: ![](https://img.kancloud.cn/da/24/da2401b6375da45d97fe060d64712c4c_1596x364.png =600x) &emsp;&emsp;第一個數字是 Engine.IO 的通信類型。 | key | value | | --- | --- | | 0 | open | | 1 | close | | 2 | ping | | 3 | pong | | 4 | message | | 5 | upgrade | | 6 | noop | &emsp;&emsp;第二個數字是 Socket.IO 的操作類型。 | key | value | | --- | --- | | 0 | CONNECT | | 1 | DISCONNECT | | 2 | EVENT | | 3 | ACK | | 4 | ERROR | | 5 | BINARY\_EVENT | | 6 | BINARY\_ACK | ## 二、附加功能 &emsp;&emsp;附加功能包括命名空間、專屬通道和適配器。 **1)命名空間(namespace)** &emsp;&emsp;命名空間是一種通信通道,允許通過單個共享連接拆分應用程序的邏輯,即多路復用,適合一臺服務器提供多條不同長連接業務的場景。 &emsp;&emsp;如下圖所示,分配了兩個命名空間,通過一條管道連接了客戶端和服務器。 :-: ![](https://img.kancloud.cn/02/8b/028b503236ec0d1b0b92acf0e6622b01_1321x326.png =800x) &emsp;&emsp;在服務端,注冊 connection 事件之前需要先調用 of() 方法,參數要和客戶端請求地址中的路徑一致。 &emsp;&emsp;注意,與之前不同的是,觸發事件的對象是 socket 而不是 io,也就是調用 socket.emit() 才能發送消息。 ~~~ const io = new Server(server); io.of("/orders").on('connection', (socket) => { socket.on('chat message', (msg) => { console.log('orders message: ' + msg); socket.emit('chat message', msg); }); }); io.of("/users").on('connection', (socket) => { socket.on('chat message', (msg) => { console.log('users message: ' + msg); socket.emit('chat message', msg); }); }); ~~~ **2)專屬通道(room)** &emsp;&emsp;room 可以建立專屬于幾條 socket 的通道,用于向一部分客戶端廣播事件,如下圖所示。 &emsp;&emsp;類似于微信群的概念,發送的消息,只能群里的人收到。 &emsp;&emsp;注意,room 是服務端的概念,客戶端是不知道 room 的存在。 &emsp;&emsp;客戶端延續命名空間中的代碼不需要改造,在服務端調用 join() 方法加入一個 room,leave() 方法可以離開一個 room。 &emsp;&emsp;然后在接收消息時調用 to() 方法給指定 room 中的 socket 發送消息,但不包括自己,效果如下圖所示。 ~~~ io.of("/orders").on('connection', (socket) => { socket.join("one room"); // 注冊接收消息事件 socket.on('chat message', (msg) => { socket.to("one room").emit('chat message', msg); }); }); ~~~ :-: ![](https://img.kancloud.cn/b4/82/b482a57846a142d289377d1e371e6939_688x331.png =500x) &emsp;&emsp;socket.to() 的效果其實就是這條消息不會讓自己收到,與 io.to() 的區別是后者可以讓自己也收到。 &emsp;&emsp;不過在調試的時候,調用 io.to() 后,不知為何,客戶端都收不到消息。 &emsp;&emsp;在做即時通信的項目時,采用 socket.to() 更合適,自己發送的消息完全可以通過腳本添加到聊天界面中。 **3)適配器(adapter)** &emsp;&emsp;適配器是一個服務端組件,負責將事件廣播到所有或部分客戶端。 &emsp;&emsp;當擴展到多個 Socket.IO 服務器時,需要集群部署時,就得將默認的內存適配器替換為另一種,例如 Redis、MongoDB 等。 &emsp;&emsp;這樣做的目的,就是為了將事件正確路由到所有客戶端。 &emsp;&emsp;在下圖中,客戶端觸發事件后,經過適配器路由到集群的 Socket.IO 服務器中。 ![](https://img.kancloud.cn/02/99/029916463caa029ac8efd378e4349ef5_1134x702.png =800x) &emsp;&emsp;以 redis 為例,首先安裝 @socket.io/redis-adapter 和 ioredis 庫,前者在 v7 版本之前叫 socket.io-redis。 &emsp;&emsp;然后是改造服務端,客戶端不用做調整,引入兩個庫。本機已安裝 redis 環境,若未安裝不知道會不會報錯。 ~~~ const { Server } = require("socket.io"); const { createAdapter } = require("@socket.io/redis-adapter"); const { Cluster } = require("ioredis"); ~~~ &emsp;&emsp;接著連接 redis 庫,調用 adapter() 方法選擇適配器。 ~~~ const io = new Server(server); const pubClient = new Cluster([ { host: "localhost", port: 6380, } ]); const subClient = pubClient.duplicate(); io.adapter(createAdapter(pubClient, subClient)); ~~~ 參考資料: [Node.js + Socket.io 實現一對一即時聊天](https://www.nodejs.red/#/nodejs/npm/private-chat-socketio) [socket.io官方文檔中文版](https://zhuanlan.zhihu.com/p/29148869) [基于socket.io構建即時通訊應用](https://zhuanlan.zhihu.com/p/95575230) [socket.io namespaces and rooms (譯)?](https://segmentfault.com/a/1190000021255876) [Socket.io源碼分析](https://zhuanlan.zhihu.com/p/27624534) ***** > 原文出處: [博客園-Node.js精進](https://www.cnblogs.com/strick/category/2154090.html) [知乎專欄-前端性能精進](https://www.zhihu.com/column/c_1611672656142725120) 已建立一個微信前端交流群,如要進群,請先加微信號freedom20180706或掃描下面的二維碼,請求中需注明“看云加群”,在通過請求后就會把你拉進來。還搜集整理了一套[面試資料](https://github.com/pwstrick/daily),歡迎瀏覽。 ![](https://box.kancloud.cn/2e1f8ecf9512ecdd2fcaae8250e7d48a_430x430.jpg =200x200) 推薦一款前端監控腳本:[shin-monitor](https://github.com/pwstrick/shin-monitor),不僅能監控前端的錯誤、通信、打印等行為,還能計算各類性能參數,包括 FMP、LCP、FP 等。
                  <ruby id="bdb3f"></ruby>

                  <p id="bdb3f"><cite id="bdb3f"></cite></p>

                    <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
                      <p id="bdb3f"><cite id="bdb3f"></cite></p>

                        <pre id="bdb3f"></pre>
                        <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

                        <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
                        <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

                        <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                              <ruby id="bdb3f"></ruby>

                              哎呀哎呀视频在线观看